{"slug": "fortran-relational-database", "title": "Fortran Relational Database", "summary": "A developer has created sqr, a self-contained relational database written entirely in Fortran, using Claude Opus 4.8 to generate approximately 13,000 lines of code. The engine supports indexed, transactional, crash-safe local storage for Fortran programs without requiring external libraries or database servers. It is designed for small-to-medium workloads and has been tested across multiple compilers including gfortran, ifx, and flang.", "body_md": "Fortran Relational Database\n\nThis is a self-contained relational database written entirely in Fortran, see the README below for details.\n\nAll the code has been written by Claude Opus 4.8 - about 13k lines in total including about 5.6k for the engine. My contribution has been the overall design, style, refactoring, testing requirements, clean builds, platform requirements and code reviews. It has had many reviews for which I used Codex, Antigravity, Fable 5 as well as Opus 4.8 in *ultrathink* mode. We even found a bug in gfortran (125866) which is being fixed.\n\nAll development was done on Linux (Mageia 9). Both ‘make’ and ‘fpm’ can be used to build it and FORD documentation is included.\n\nIt is known to build and run on Linux with ifx 2026.0.0, gfortran 16.1.0, flang 23.0.0git ; on Windows with mingw64 and tested with Wine in podman\n\nSo where might this be useful? In keeping with the development I asked Claude:\n\nA Fortran program that needs indexed, transactional, queryable local storage — but doesn’t want to leave the language, link C/SQLite, or stand up a database server. Anywhere you’d currently reach for ad-hoc flat files, unformatted dumps, or a fragile NetCDF/CSV scheme, sqr offers indexed lookup + crash-safety instead.\n\n```\n                         Abridged README.md\n```\n\n`sqr`\n\nis a lightweight, embeddable relational storage engine written entirely\n\nin modern Fortran. It stores tables as fixed-record binary files in a\n\ndirectory, with on-disk B+tree secondary indices, a physical rollback\n\njournal for crash-safe transactions, and two interactive front-ends — a\n\nstate-graph shell (`sqrsh`\n\n) and a small SQL-subset REPL (`sqlsh`\n\n).\n\nIt is deliberately scoped for the small-to-medium workloads a single program\n\nneeds (10⁴–10⁶ rows), not for postgres-scale concurrency. Access is\n\n**single-writer / multi-reader**: an advisory lock admits one read-write\n\nconnection *or* any number of read-only ones at a time — there is no concurrent-writer (per-row / MVCC) isolation. The design goal is **integrity first**: every mutation is write-ahead journalled and survives a crash, even at the cost of an `fsync`\n\nper write.\n\n`DT_INT`\n\n(32-bit), `DT_REAL`\n\n(64-bit), `DT_CHAR`\n\n`DT_TEXT`\n\n(length-prefixed blob, binary-safe).`b_tree`\n\nmodule is fully decoupled from `sqr`\n\nand is`ADD COLUMN`\n\n/ `DROP COLUMN`\n\nby table rewrite, with`DROP`\n\ncascading to dependent`db_begin`\n\n/`db_commit`\n\n/`db_rollback`\n\n,`SQR_LOCKED`\n\n,`db_set_readonly`\n\ndemotes a writer to let readers in.`error stop`\n\nin library code`stat`\n\n/ `errmsg`\n\narguments.`db_insert(db, ...)`\n\nor`db%insert(...)`\n\n.`sqrsh`\n\n, a cmdgraph state-graph shell over the engine,`sqlsh`\n\n, a small SQL subset (a separate `sql`\n\nfront-end layer that`ifx`\n\nand `gfortran`\n\n, builds under `fpm`\n\n, and\n\n```\nuse :: sqr\ntype(db_t), target :: db\ntype(column_t)     :: cols(2)\ncharacter(len=:), allocatable :: buf\ninteger        :: st, ti\ninteger(int32) :: rid\n\ncall db_open(db, 'mydb', stat=st)                 ! a database is a directory\n\ncols(1)%name = 'id';   cols(1)%dtype = DT_INT;  cols(1)%csize = 4\ncols(2)%name = 'name'; cols(2)%dtype = DT_CHAR; cols(2)%csize = 32\ncall db_create_table(db, 'people', cols, st)\nti = db_table_index(db, 'people')\n\ncall row_alloc(buf, db%tables(ti)%record_size)\ncall row_set_int (buf, db%tables(ti)%cols(1), 1_int32)\ncall row_set_char(buf, db%tables(ti)%cols(2), 'Ada')\ncall db_insert(db, 'people', buf, rid, st)        ! rid = new row id\n\ncall db_create_index(db, 'people', 'id', st)      ! on-disk B+-tree\ncall db_find_by_int(db, 'people', 'id', 1_int32, rid, st)\n\ncall db_close(db, st)\n```\n\nWrap a group of changes in an explicit transaction when you need them to\n\ncommit (and fail) as a unit:\n\n```\ncall db_begin(db, st)\n! ... several inserts / updates / deletes ...\ncall db_commit(db, st)     ! durable here; or db_rollback(db, st)\n```\n\n`sqrsh`\n\nshell`sqrsh`\n\nis a small state-graph REPL over the engine. The command set:\n\n```\nroot:    open <dir>   close   readonly   tables   desc <table>\n         create <table>   use <table>   drop <table>   quit\ncreator: col <name> <type>   done   cancel   quit\ntable:   insert ...   select   get <id>   delete <id>   compact\n         addcolumn <name> <type>   dropcolumn <name>\n         index [unique] <col>...   dropindex <col>...   verify\n         find <col> <value>   range <col> <lo> <hi>   match <col> <regex>\n         getk ...   delk ...   back   quit\n```\n\n`sqlsh`\n\nshell (SQL subset)`sqlsh`\n\nis a second, independent front-end: a familiar SQL “shop window”\n\nover the same engine. It is a **front-end layer only** — the `sql`\n\nmodule\n\n(lexer, parser, executor) and the REPL call nothing but the public `db_*`\n\nAPI, so the dependency runs one way (`sql`\n\nuses `sqr`\n\n, never the reverse)\n\nand nothing about the on-disk format changes. The store itself has no\n\nnotion of SQL.\n\n```\nsqlsh mydb < script.sql        # run a script (results on stdout)\nsqlsh mydb                     # interactive (prompts/errors on stderr)\n```\n\nMeta-commands: `.open <dir>`\n\n, `.close`\n\n, `.tables`\n\n, `.schema [table]`\n\n,\n\n`.help`\n\n, `.quit`\n\n. Everything else is SQL:\n\n```\nCREATE TABLE employee (id INTEGER, name CHAR(20), dept CHAR(12), salary REAL);\nCREATE INDEX ON employee (dept);\nINSERT INTO employee VALUES (1,'Alice','eng',55000.0), (2,'Bob','eng',48000.0);\nSELECT name, salary FROM employee WHERE dept = 'eng' ORDER BY salary DESC LIMIT 5;\nUPDATE employee SET salary = 50000.0 WHERE dept = 'sales' AND salary < 50000.0;\nDELETE FROM employee WHERE salary < 40000.0;\n```\n\nA database is a directory containing:\n\n| File | Contents |\n|---|---|\n`_catalog.dat` |\ntop-level catalog: the list of table names |\n`<table>.schema` |\nper-table schema header + column definitions |\n`<table>.dat` |\nfixed-size records (`recl = record_size` ) |\n`<table>.blob` |\nlength-prefixed `DT_TEXT` values |\n`<table>__i<slot>.idx` |\none paged B±tree per secondary index |\n`_journal.dat` |\nrollback (undo) journal; present only while a txn is open or pending recovery |\n`_lock` |\nzero-byte sentinel carrying the advisory open lock |\n\nEach record is: 1 status byte (`ROW_ALIVE`\n\n/ `ROW_TOMBSTONE`\n\n), then a\n\n`(ncols+7)/8`\n\n-byte NULL bitmap, then column data at fixed offsets.\n\n`sqr`\n\nis honest about what it provides. The store is **single-writer /\nmulti-reader**: at most one read-write connection, or any number of\n\n`db_open`\n\n. It does | Property | Status | How |\n|---|---|---|\nAtomicity |\nYes | Physical undo journal; a failed or rolled-back transaction restores every touched region. |\nConsistency |\nYes, including across a crash | Recovery on `db_open` replays the journal, restoring data, blob and index files together. |\nDurability |\nYes | Strict write-ahead: each undo image is `fsync` ’d to the hot journal before the base write it guards. Commit `fsync` s every modified file, then voids the journal header — the single durable commit point. |\nIsolation |\n◑ Coarse | An advisory lock on `_lock` admits one writer xor many readers. A second writer (or a reader while a writer is active) is refused with `SQR_LOCKED` . `db_set_readonly` downgrades a writer so readers may attach. No concurrent-writer / row-level isolation. |\n\nLocking is whole-database advisory: `flock(2)`\n\non POSIX, `LockFileEx`\n\non\n\nWindows. It is released on `db_close`\n\nand automatically by the OS if the\n\nprocess dies, so a crashed writer never wedges the database.\n\nThe durability path is deliberately conservative — an `fsync`\n\nper write —\n\nbecause the project prioritises integrity over throughput.", "url": "https://wpnews.pro/news/fortran-relational-database", "canonical_source": "https://fortran-lang.discourse.group/t/fortran-relational-database/10974#post_1", "published_at": "2026-06-18 20:30:47+00:00", "updated_at": "2026-06-18 20:33:27.604045+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Claude Opus 4.8", "Codex", "Antigravity", "Fable 5", "gfortran", "ifx", "flang", "Mageia 9"], "alternates": {"html": "https://wpnews.pro/news/fortran-relational-database", "markdown": "https://wpnews.pro/news/fortran-relational-database.md", "text": "https://wpnews.pro/news/fortran-relational-database.txt", "jsonld": "https://wpnews.pro/news/fortran-relational-database.jsonld"}}