{"id":47890412,"url":"https://github.com/RichardKnop/minisql","last_synced_at":"2026-04-12T06:01:10.548Z","repository":{"id":283907356,"uuid":"864161742","full_name":"RichardKnop/minisql","owner":"RichardKnop","description":"Embedded single file SQL database written in Golang","archived":false,"fork":false,"pushed_at":"2026-04-05T00:37:48.000Z","size":1413,"stargazers_count":31,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-04-05T01:20:20.356Z","etag":null,"topics":["agentic-ai","agentic-workflow","database","golang","single-file","sql","sqlite"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RichardKnop.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2024-09-27T15:54:01.000Z","updated_at":"2026-04-05T00:37:52.000Z","dependencies_parsed_at":"2026-03-26T03:03:48.196Z","dependency_job_id":null,"html_url":"https://github.com/RichardKnop/minisql","commit_stats":null,"previous_names":["richardknop/minisql"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/RichardKnop/minisql","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RichardKnop%2Fminisql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RichardKnop%2Fminisql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RichardKnop%2Fminisql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RichardKnop%2Fminisql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RichardKnop","download_url":"https://codeload.github.com/RichardKnop/minisql/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RichardKnop%2Fminisql/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31705574,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-12T05:11:36.334Z","status":"ssl_error","status_checked_at":"2026-04-12T05:11:27.332Z","response_time":58,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agentic-ai","agentic-workflow","database","golang","single-file","sql","sqlite"],"created_at":"2026-04-04T03:00:37.433Z","updated_at":"2026-04-12T06:01:10.536Z","avatar_url":"https://github.com/RichardKnop.png","language":"Go","readme":"# minisql\n\n[![CI Status](https://github.com/RichardKnop/minisql/actions/workflows/go.yml/badge.svg)](https://github.com/RichardKnop/minisql/actions/workflows/go.yml)\n[![Go Report Card](https://goreportcard.com/badge/github.com/RichardKnop/minisql)](https://goreportcard.com/report/github.com/RichardKnop/minisql)\n\n`MiniSQL` is an embedded single file database written in Golang, inspired by `SQLite` (however borrowing some features from other databases as well). It originally started as a research project aimed at learning about internals of relational databases. Over time it has progressed and grown to its current form. It is a very early stage project and it might contain bugs and is not battle tested. Please employ caution when using this database.\n\n[![Donate Bitcoin](https://img.shields.io/badge/donate-bitcoin-orange.svg)](https://richardknop.github.io/donate/)\n\nTo use minisql in your Go code, import the driver:\n\n```go\nimport (\n  _ \"github.com/RichardKnop/minisql\"\n)\n```\n\nAnd create a database instance:\n\n```go\n// Simple path\ndb, err := sql.Open(\"minisql\", \"./my.db\")\n\n// With connection parameters\ndb, err := sql.Open(\"minisql\", \"./my.db?journal=false\")\n\n// Multiple parameters\ndb, err := sql.Open(\"minisql\", \"./my.db?journal=true\u0026log_level=debug\")\n```\n\n## Connection Pooling\n\n**MiniSQL is an embedded, single-file database (like SQLite).** Always configure your connection pool to use a single connection and serialize all writes through it.\n\n```go\ndb.SetMaxOpenConns(1)\ndb.SetMaxIdleConns(1)\n```\n\n**Why?** Multiple connections to the same file cause:\n- Lock contention at the OS level\n- Connection pool overhead (97% lock time in benchmarks)\n- No performance benefit (writes are serialized anyway)\n\nThis is the same recommendation as SQLite.\n\n## Connection String Parameters\n\nMiniSQL supports optional connection string parameters:\n\n| Parameter | Values | Default | Description |\n|-----------|--------|---------|-------------|\n| `journal` | `true`, `false` | `true` | Enable/disable rollback journal for crash recovery |\n| `log_level` | `debug`, `info`, `warn`, `error` | `warn` | Set logging verbosity level |\n| `max_cached_pages` | positive integer | `2000` | Maximum number of pages to keep in memory cache |\n\n**Examples:**\n```go\n// Disable journaling for better performance (no crash recovery)\ndb, err := sql.Open(\"minisql\", \"./my.db?journal=false\")\n\n// Enable debug logging\ndb, err := sql.Open(\"minisql\", \"./my.db?log_level=debug\")\n\n// Set cache size to 500 pages (~2MB memory)\ndb, err := sql.Open(\"minisql\", \"./my.db?max_cached_pages=500\")\n\n// Combine multiple parameters\ndb, err := sql.Open(\"minisql\", \"/path/to/db.db?journal=true\u0026log_level=info\u0026max_cached_pages=2000\")\n```\n\n**Note:** Disabling journaling (`journal=false`) improves performance but removes crash recovery protection. If the application crashes during a transaction commit, the database may become corrupted.\n\n##  Write-Ahead Rollback Journal\n\nMiniSQL uses a [rollback journal](https://sqlite.org/lockingv3.html#rollback) to achieve atomic commit and rollback . Before committing a transaction,a journal file with `-journal` suffix is created in the same directory as the database file. It contains original state of the pages (and database header if applicable) before the transaction began. In case of an error encountered during flushing of pages changed by the transaction to the disk, the database quits and recovers to original state before the transaction from the journal file.\n\n## Storage\n\nEach page size is `4096 bytes`. Rows larger than page size are not supported. Therefore, the largest allowed inline row size is `4065 bytes` (with exception of root page 0 which has first 100 bytes reserved for config). Variable text colums can use overflow pages and are not limited by page size.\n\n```\n4096 (page size) \n- 7 (base header size) \n- 8 (internal / leaf node header size) \n- 8 (null bit mask) \n- 8 (internal row ID / key) \n= 4065\n```\n\nAll tables are kept track of via a system table `minisql_schema` which contains table name, `CREATE TABLE` SQL to document table structure and a root page index indicating which page contains root node of the table B+ Tree.\n\nEach row has an internal row ID which is an unsigned 64 bit integer starting at 0. These are used as keys in B+ Tree data structure. \n\nMoreover, each row starts with 64 bit null mask which determines which values are NULL. Because of the NULL bit mask being an unsigned 64 bit integer, there is a limit of `maximum 64 columns per table`.\n\n## Concurrency\n\nMiniSQL uses `Optimistic Concurrency Control` or `OCC`. It is close to PostgreSQL's [SERIALIZABLE isolation](https://www.postgresql.org/docs/current/transaction-iso.html#XACT-SERIALIZABLE) Transaction manager follows a simple process:\n\n1. Track read versions - Record which version of each page was read\n2. Check at commit time - Verify no pages were modified during the transaction\n3. Abort on conflict - If a page changed, abort with ErrTxConflict\n\nYou can use `ErrTxConflict` to control whether to retry because of a tx serialization error or to return error.\n\nSQlite uses a `snapshot isolation` with `MVCC` (`Multi-Version Concurrency Control`). Read how [SQLite handles isolation](https://sqlite.org/isolation.html). I have chosen a basic OCC model for now for its simplicity.\n\nExample of a snapshot isolation:\n\n```\nTime 0: Read TX1 starts, sees version V1\nTime 1: Write TX2 modifies page and commits → creates version V2\nTime 2: TX1 continues reading, still sees V1 (not V2!)\nTime 3: TX1 completes successfully\n```\n\n## System Table\n\nAll tables and indexes are tracked in the system table `minisql_schema`. For empty database, it would contain only its own reference:\n\n```sh\n type   | name               | table_name         | root_page   | sql                                                \n--------+--------------------+--------------------+-------------+----------------------------------------\n 1      | minisql_schema     |                    | 0           | create table \"minisql_schema\" (        \n        |                    |                    |             | \ttype int4 not null,                  \n        |                    |                    |             | \tname varchar(255) not null,          \n        |                    |                    |             | \ttable_name varchar(255),             \n        |                    |                    |             | \troot_page int4,                      \n        |                    |                    |             | \tsql text                             \n        |                    |                    |             | )                                      \n```\n\nLet's say you create a table such as:\n\n```sql\ncreate table \"users\" (\n\tid int8 primary key autoincrement,\n\temail varchar(255) unique,\n\tname text,\n\tage int4,\n\tcreated timestamp default now()\n);\ncreate index \"idx_created\" on \"users\" (\n\tcreated\n);\n```\n\nIt will be added to the system table as well as its primary key and any unique or secondary indexes. Secondary index on `created TIMESTAMP` column created separately will also be added to the system table.\n\nYou can check current objects in the `minisql_schema` system table by a simple `SELECT` query.\n\n```go\n// type schema struct {\n// \tType      int\n// \tName      string\n// \tTableName *string\n// \tRootPage  int\n// \tSql       *string\n// }\n\nrows, err := db.QueryContext(context.Background(), `select * from minisql_schema;`)\nif err != nil {\n\treturn err\n}\ndefer rows.Close()\n\nvar schemas []schema\nfor rows.Next() {\n\tvar aSchema schema\n\tif err := rows.Scan(\u0026aSchema.Type, \u0026aSchema.Name, \u0026aSchema.TableName, \u0026aSchema.RootPage, \u0026aSchema.SQL); err != nil {\n\t\treturn err\n\t}\n\tschemas = append(schemas, aSchema)\n}\nif err := rows.Err(); err != nil {\n\treturn err\n}\n```\n\n```sh\n type   | name               | table_name         | root_page   | sql                                                \n--------+--------------------+--------------------+-------------+----------------------------------------\n 1      | minisql_schema     |                    | 0           | create table \"minisql_schema\" (        \n        |                    |                    |             | \ttype int4 not null,                  \n        |                    |                    |             | \tname varchar(255) not null,          \n        |                    |                    |             | \ttable_name varchar(255),             \n        |                    |                    |             | \troot_page int4,                      \n        |                    |                    |             | \tsql text                             \n        |                    |                    |             | )                                      \n 1      | users              |                    | 1           | create table \"users\" (                 \n        |                    |                    |             | \tid int8 primary key autoincrement,   \n        |                    |                    |             | \temail varchar(255) unique,           \n        |                    |                    |             | \tname text,                           \n        |                    |                    |             | \tage int4,                            \n        |                    |                    |             | \tcreated timestamp default now()      \n        |                    |                    |             | );                                     \n 2      | pkey__users        | users              | 2           | NULL                                   \n 3      | key__users_email   | users              | 3           | NULL                                   \n 4      | idx_users          | users              | 4           | create index \"idx_created\" on \"users\" (             \n        |                    |                    |             | \tcreated,                             \n        |                    |                    |             | );                                     \n```\n\n## Data Types And Storage\n\n| Data type    | Description |\n|--------------|-------------|\n| `BOOLEAN`    | 1-byte boolean value (true/false). |\n| `INT4`       | 4-byte signed integer (-2,147,483,648 to 2,147,483,647). |\n| `INT8`       | 8-byte signed integer (-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807). |\n| `REAL`       | 4-byte single-precision floating-point number. |\n| `DOUBLE`     | 8-byte double-precision floating-point number. |\n| `TEXT`       | Variable-length text. If length is \u003c= 255, the text is stored inline, otherwise text is stored in overflow pages (with UTF-8 encoding). |\n| `VARCHAR(n)` | Storage works the same way as `TEXT` but allows limiting length of inserted/updated text to max value. |\n| `TIMESTAMP`  | 8-byte signed integer representing number of microseconds from `2000-01-01 00:00:00 UTC` (`Postgres epoch`). Supported range is from `4713 BC` to `294276 AD` inclusive. |\n\n## TIMESTAMP Spec\n\nMiniSQL `TIMESTAMP` is a timestamp-without-time-zone type. It stores a calendar date and wall-clock time with microsecond precision, but it does not store or interpret any timezone offset.\n\n- Storage format: signed 64-bit integer counting microseconds since `2000-01-01 00:00:00 UTC` (the PostgreSQL epoch).\n- Precision: microseconds. Fractional seconds from 1 to 6 digits are accepted and are scaled to microseconds.\n- Calendar model: proleptic Gregorian calendar for the full supported range.\n- Supported range: `4713-01-01 00:00:00 BC` through `294276-12-31 23:59:59.999999`.\n- BC handling: input and output use PostgreSQL-style ` BC` suffix. Internally, astronomical year numbering is used (`1 BC` = year `0`, `2 BC` = year `-1`).\n- `NOW()`: evaluated in UTC and stored as a timezone-naive timestamp value.\n\nAccepted literal forms:\n\n- `YYYY-MM-DD HH:MM:SS`\n- `YYYY-MM-DD HH:MM:SS.f`\n- `YYYY-MM-DD HH:MM:SS.ff`\n- `YYYY-MM-DD HH:MM:SS.ffffff`\n- Any of the above with trailing ` BC`\n\nExamples:\n\n```sql\n'2024-03-15 10:30:45'\n'2024-03-15 10:30:45.1'\n'2024-03-15 10:30:45.123456'\n'0001-12-31 23:59:59.999999 BC'\n```\n\nImportant behavior and current non-goals:\n\n- Timezone-qualified values are rejected. Examples: `Z`, `UTC`, `GMT`, `+01:00`, `-05:30`.\n- Leap seconds are not supported. Seconds must be in the range `00` to `59`.\n- Year `0000` is rejected in input. Use `0001 ... BC` for 1 BC.\n- MiniSQL does not currently support `TIMESTAMP WITH TIME ZONE`.\n- String formatting normalizes fractional precision to either no fractional part or exactly 6 fractional digits.\n\n## SQL Features\n\n| Feature | Notes |\n|---------|-------|\n| `CREATE TABLE`, `CREATE TABLE IF NOT EXISTS` | |\n| `PRIMARY KEY` | Single column only; no composite primary keys |\n| `AUTOINCREMENT` | Primary key must be of type `INT8` |\n| `UNIQUE` | Can be specified when creating a table |\n| Composite primary key or unique constraint | As part of `CREATE TABLE` |\n| `NULL` and `NOT NULL` | Via null bit mask included in each row/cell |\n| `DEFAULT` | Supported for all columns, including `NOW()` for `TIMESTAMP` |\n| `DROP TABLE` | |\n| `CREATE INDEX`, `DROP INDEX` | Secondary non-unique indexes only; primary and unique indexes are declared as part of `CREATE TABLE` |\n| `INSERT` | Single row or multiple rows via a tuple of values separated by commas |\n| `ON CONFLICT` | Both `DO NOTHING` and `DO UPDATE` supported (with `EXCLUDED` pseudo table syntax for updating) |\n| `SELECT` | All fields with `*`, specific fields, or row count with `COUNT(*)` |\n| `SELECT DISTINCT` | |\n| `JOIN` | `INNER`, `LEFT` and `RIGHT` joins supported. Star schema only — one or more tables joined with the base table |\n| `UPDATE` | |\n| `DELETE` | |\n| `WHERE` | Operators: `=`, `!=`, `\u003e`, `\u003e=`, `\u003c`, `\u003c=`, `IN`, `NOT IN`, `LIKE`, `NOT LIKE`, `BETWEEN` |\n| `LIKE`, `NOT LIKE` | `%` matches any sequence of zero or more characters; `_` matches any single character |\n| `LIMIT` and `OFFSET` | Basic pagination |\n| `ORDER BY` | Single column only |\n| `GROUP BY` and `HAVING` | Aggregate functions: `COUNT`, `MAX`, `MIN`, `SUM`, `AVG` |\n| `VACUUM` | Rebuilds the database file, repacking it into a minimal amount of disk space (similar to SQLite) |\n\nPrepared statements are supported using `?` as a placeholder. For example:\n\n```sql\ninsert into users(\"name\", \"email\") values(?, ?), (?, ?);\n```\n\n## DDL SQL Commands\n\n### CREATE TABLE\n\nLet's start by creating your first table:\n\n```go\n_, err := db.Exec(`create table \"users\" (\n\tid int8 primary key autoincrement,\n\temail varchar(255) unique,\n\tname text,\n\tage int4,\n\tcreated timestamp default now()\n);`)\n```\n\n### DROP TABLE\n\n```go\n_, err := db.Exec(`drop table \"users\";`)\n```\n\n### CREATE INDEX\n\nCurrently you can only create secondary non unique indexes. Unique and primary index can be created as part of `CREATE TABLE`.\n\n```go\n_, err := db.Exec(`create index \"idx_created\" on \"users\" (created);`)\n```\n\n### DROP INDEX\n\nCurrently you can only drop secondary non unique indexes.\n\n```go\n_, err := db.Exec(`drop index \"idx_created\";`)\n```\n\n## DML Commands\n\n### INSERT\n\nInsert test rows:\n\n```go\ntx, err := s.db.Begin()\nif err != nil {\n\treturn err\n}\naResult, err := tx.ExecContext(context.Background(), `insert into users(\"email\", \"name\", \"age\") values('Danny_Mason2966@xqj6f.tech', 'Danny Mason', 35),\n('Johnathan_Walker250@ptr6k.page', 'Johnathan Walker', 32),\n('Tyson_Weldon2108@zynuu.video', 'Tyson Weldon', 27),\n('Mason_Callan9524@bu2lo.edu', 'Mason Callan', 19),\n('Logan_Flynn9019@xtwt3.pro', 'Logan Flynn', 42),\n('Beatrice_Uttley1670@1wa8o.org', 'Beatrice Uttley', 32),\n('Harry_Johnson5515@jcf8v.video', 'Harry Johnson', 25),\n('Carl_Thomson4218@kyb7t.host', 'Carl Thomson', 53),\n('Kaylee_Johnson8112@c2nyu.design', 'Kaylee Johnson', 48),\n('Cristal_Duvall6639@yvu30.press', 'Cristal Duvall', 27);`)\nif err != nil {\n\treturn err\n}\nrowsAffected, err = aResult.RowsAffected()\nif err != nil {\n\treturn err\n}\n// rowsAffected = 10\nif err := tx.Commit(); err != nil {\n\tif errors.Is(err, minisql.ErrTxConflict) {\n\t\t// transaction conflict, you might want to retry here\n\t}\n\treturn err\n}\n```\n\nWhen trying to insert a duplicate primary key, you will get an error:\n\n```go\n_, err := db.ExecContext(context.Background(), `insert into users(\"id\", \"name\", \"email\", \"age\") values(1, 'Danny Mason', 'Danny_Mason2966@xqj6f.tech', 35);`)\nif err != nil {\n\tif errors.Is(err, minisql.ErrDuplicateKey) {\n\t\t// handle duplicate primary key\n\t}\n\treturn err\n}\n```\n\n### SELECT\n\nSelecting from the table:\n\n```go\n// type user struct {\n// \tID      int64\n// \tEmail   string\n// \tName    string\n// \tCreated time.Time\n// }\n\nrows, err := db.QueryContext(context.Background(), `select * from users;`)\nif err != nil {\n\treturn err\n}\ndefer rows.Close()\nvar users []user\nfor rows.Next() {\n\tvar aUser user\n\terr := rows.Scan(\u0026aUser.ID, \u0026aUser.Name, \u0026aUser.Email, \u0026aUser.Created)\n\tif err != nil {\n\t\treturn err\n\t}\n\tusers = append(users, aUser)\n}\nif err := rows.Err(); err != nil {\n\treturn err\n}\n// continue\n```\n\nTable should have 10 rows now:\n\n```sh\n id     | email                            | name                    | age    | created                       \n--------+----------------------------------+-------------------------+--------+-------------------------------\n 1      | Danny_Mason2966@xqj6f.tech       | Danny Mason             | 35     | 2025-12-21 22:31:35.514831    \n 2      | Johnathan_Walker250@ptr6k.page   | Johnathan Walker        | 32     | 2025-12-21 22:31:35.514831    \n 3      | Tyson_Weldon2108@zynuu.video     | Tyson Weldon            | 27     | 2025-12-21 22:31:35.514831    \n 4      | Mason_Callan9524@bu2lo.edu       | Mason Callan.           | 19     | 2025-12-21 22:31:35.514831    \n 5      | Logan_Flynn9019@xtwt3.pro        | Logan Flynn             | 42     | 2025-12-21 22:31:35.514831    \n 6      | Beatrice_Uttley1670@1wa8o.org    | Beatrice Uttley         | 32     | 2025-12-21 22:31:35.514831    \n 7      | Harry_Johnson5515@jcf8v.video    | Harry Johnson.          | 25     | 2025-12-21 22:31:35.514831    \n 8      | Carl_Thomson4218@kyb7t.host      | Carl Thomson            | 53     | 2025-12-21 22:31:35.514831    \n 9      | Kaylee_Johnson8112@c2nyu.design  | Kaylee Johnson.         | 48     | 2025-12-21 22:31:35.514831    \n 10     | Cristal_Duvall6639@yvu30.press   | Cristal Duvall.         | 27     | 2025-12-21 22:31:35.514831    \n```\n\nYou can also count rows in a table:\n\n```go\nvar count int\nif err := db.QueryRow(`select count(*) from users;`).Scan(\u0026count); err != nil {\n\treturn err\n}\n```\n\n#### INNER JOIN (star schema)\n\nThere is an experimental support for `INNER JOIN`, however it only supports star schema joins, i.e. one or more tables joined with the base table. Nested joins and other types of joins such as `LEFT`, `RIGHT` are not supported yet.\n\n### UPDATE\n\nLet's try using a prepared statement to update a row:\n\n```go\nstmt, err := db.Prepare(`update users set age = ? where id = ?;`)\nif err != nil {\n\treturn err\n}\naResult, err := stmt.Exec(int64(36), int64(1))\nif err != nil {\n\treturn err\n}\nrowsAffected, err = aResult.RowsAffected()\nif err != nil {\n\treturn err\n}\n// rowsAffected = 1\n```\n\nSelect to verify update:\n\n```sh\n id     | email                            | name                    | age    | created                       \n--------+----------------------------------+-------------------------+--------+-------------------------------\n 1      | Danny_Mason2966@xqj6f.tech       | Danny Mason             | 36     | 2025-12-21 22:31:35.514831    \n```\n\n### DELETE\n\nYou can also delete rows:\n\n```go\n_, err := db.ExecContext(context.Background(), `delete from users;`)\nif err != nil {\n\treturn err\n}\nrowsAffected, err = aResult.RowsAffected()\nif err != nil {\n\treturn err\n}\n```\n\n## Development \n\nMiniSQL uses [mockery](https://github.com/vektra/mockery) to generate mocks for interfaces. Install mockery:\n\n```sh\ngo install github.com/vektra/mockery/v3@v3.6.1\n```\n\nThen to generate mocks:\n\n```sh\nmockery\n```\n\nTo run unit tests:\n\n```sh\nLOG_LEVEL=info go test ./... -count=1\n```\n\nSetting the `LOG_LEVEL` to `info` makes sure to supress debug logs and makes potential error messages in tests easier to read and debug.\n\n### Benchmarking\n\n\nSome benchmarking commands I have used just for my own reference.\n\n```sh\ngo test -bench=BenchmarkPageAccess -benchtime=100000x ./internal/minisql 2\u003e\u00261 | grep -A 20 \"Benchmark\"\ngo test -bench=BenchmarkRow -benchmem ./internal/minisql 2\u003e\u00261 | grep -E \"(Benchmark|B/op)\"\ngo test -bench=BenchmarkFlush -benchmem -benchtime=5s ./internal/minisql 2\u003e\u00261 | grep -E \"(Benchmark|B/op)\"\ngo test -bench=BenchmarkFlush -benchmem -benchtime=3s ./internal/minisql 2\u003e\u00261 | grep -E \"(Benchmark|B/op)\"\ngo test -bench=BenchmarkPageAccess -benchmem ./internal/minisql 2\u003e\u00261 | grep -E \"Benchmark|alloc\"\n\n# CPU profile concurrent workload\ngo test -cpuprofile=cpu.prof -bench=BenchmarkConcurrent -benchtime=10s ./e2e_tests\ngo tool pprof -top cpu.prof | head -30\n\n# When CPU profile is dominated by runtime scheduling overhead, look at specific database operations\ngo tool pprof -cum -top cpu_reads.prof | grep \"minisql\" | head -20\n\n# Memory profile\ngo test -memprofile=mem.prof -bench=BenchmarkConcurrent -benchtime=10s ./e2e_tests  \ngo tool pprof -alloc_space -top mem.prof | head -30\n\n# Mutex contention\ngo test -mutexprofile=mutex.prof -bench=BenchmarkConcurrent -benchtime=10s ./e2e_tests\ngo tool pprof -top mutex.prof | head -25\n\n```\n\n## Acknowledgements \n\nShout out to some great repos and other resources that were invaluable while figuring out how to get this all working together:\n- [Let's Build a Simple Database](https://cstack.github.io/db_tutorial/parts/part1.html)\n- [go-sqldb](https://github.com/auxten/go-sqldb)\n- [sqlparser](https://github.com/marianogappa/sqlparser)\n- [sqlite docs](https://www.sqlite.org/fileformat2.html) (section about file format has been especially useful)\n- [C++ implementation of B+ tree](https://github.com/sayef/bplus-tree)\n","funding_links":[],"categories":["Database"],"sub_categories":["Databases Implemented in Go"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRichardKnop%2Fminisql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FRichardKnop%2Fminisql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRichardKnop%2Fminisql/lists"}