{"id":23907441,"url":"https://github.com/louis77/sql_c3","last_synced_at":"2025-10-13T15:36:49.966Z","repository":{"id":269487984,"uuid":"907171887","full_name":"louis77/sql_c3","owner":"louis77","description":"A C3 SQL package with support for PostgreSQL, MySQL, SQLite","archived":false,"fork":false,"pushed_at":"2025-01-06T09:41:42.000Z","size":196,"stargazers_count":7,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-04T20:12:13.470Z","etag":null,"topics":["c3","mysql","postgresql","sqlite"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/louis77.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}},"created_at":"2024-12-23T01:57:59.000Z","updated_at":"2025-03-19T05:46:17.000Z","dependencies_parsed_at":"2024-12-23T23:37:36.538Z","dependency_job_id":null,"html_url":"https://github.com/louis77/sql_c3","commit_stats":null,"previous_names":["louis77/sql_c3"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/louis77%2Fsql_c3","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/louis77%2Fsql_c3/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/louis77%2Fsql_c3/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/louis77%2Fsql_c3/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/louis77","download_url":"https://codeload.github.com/louis77/sql_c3/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248351452,"owners_count":21089270,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["c3","mysql","postgresql","sqlite"],"created_at":"2025-01-05T03:11:55.855Z","updated_at":"2025-10-13T15:36:44.914Z","avatar_url":"https://github.com/louis77.png","language":null,"readme":"# sql_c3\n\nA C3 SQL package, implementing an interface, types and utilities to handle SQL connections across different database vendors in a unified way.\n\nDatabase drivers can implement the `sql::Driver` interface and register themselves with the `sql` module. The `sql` module can then be used to create a connection and execute queries.\n\nThe database bindings have contributed to the [C3 vendor](https://github.com/c3lang/vendor) project. See [Installing](#installing) for instructions how to obtain them.\n\n**This project is WIP, use at your own risk.**\n\n## \u003ca name=\"installing\"\u003e\u003c/a\u003eInstalling\n\n- Make sure you have the latest C3 compiler installed (0.6.6+)\n- Clone the repository\n- Fetch project dependencies\n- Optionally, run the tests\n\n```sh\ngit clone https://github.com/louis77/sql_c3\ncd sql_c3\n\n# Get bindings for pq, mysql8 and sqlite3 from vendor library\nc3c project fetch\n\n# Run tests\nc3c test\n\n# Run tests with memory leak check\nc3c test -D MEMCHECK\n```\n\nMake sure to change the linker paths in `project.json` to point to your `libpq`, `mysql-client` or `sqlite` installation.\n\n## Supported databases\n\n- [X] PostgreSQL (driver_id: `postgres`) (must install `libpq`)\n- [X] MySQL 8+ (driver_id: `mysql`) (must install `mysql-client`)\n- [X] SQLite 3+  (driver_id: `sqlite`) (must install `sqlite3`)\n\n## Usage\n\nThis package contains the following modules:\n\n- `sql` The user-facing interface\n- `pg` Driver implementation for PostgreSQL (based on `libpq`)\n- `mysql8` Driver implementation for MySQL 8.x (based on `mysql-client@8`)\n- `sqlite3` Driver implementation for SQLite 3 (basen on `sqlite3`)\n\nThe driver implementations are based on the bindings for the respective databases.\n\nGenerally, this is how you would use the `sql` module:\n\n```kotlin\nimport sql;\n\nfn void main()\n{\n\t// Open a connection\n\tsql::Connection conn = sql::open(\"postgres\", \"postgres://postgres@localhost/postgres\")!;\n\tdefer try conn.close();\n\n\t// Make a simple query\n\tString cmd = \"SELECT * FROM (VALUES(1, 'hello', null), (2, 'world', null)) AS t(a_num, a_string, a_null)\";\n\n\tResult res = conn.query(cmd)!;\n\tdefer res.close(); // Don't forget to close the result\n\n\t// Call next() to move to next row. Must also be called for the first row\n\twhile (res.next()) {\n\t\tint a_num;\n\t\tString a_string;\n\t\tint* a_null; // supports checking for NULL\n\n\t\t// Scan each column into a variable.\n\t\tres.scan(a_num, a_string, a_null)!;\n\t}\n}\n```\n\nSee the [test](test) folder for further examples of how to use the `sql` module.\n\n## Connection Pool\n\nThere is a simple implementation of a connection pool available. A pool starts a seperate thread that handles the expiration of unused connections. So your platform must either support POSIX threads or Windows.\n\nUsage:\n\n```kotlin\n\n// Instead of a connection, create a pool.\n// You pass the maximum number of connections and the expiration\n// time of unused connection.\n\nsql::Pool pool = sql::create_pool(\"postgres\", \"postgres://postgres@localhost/postgres\",\n\t16, // maximum open connections\n\t1000 * time::MS // idle timeout\n\t)!;\ndefer try pool.free();\n\n// Now you can acquire a new connection.\n// If the pool is full, it throws a sql::Error.POOL_EXHAUSTED fault\n\nsql::Connection conn = pool.acquire()!;\n\n[... do some work ...]\n\npool.release(conn)!;\n\n// After a connection is released, it will be kept open\n// until the idle timeout is reached\n```\n\n⚠️ Due to a minor bug in C3 \u003c= 0.6.5, pools leak a little bit of memory. This has been [fixed in C3 0.6.6+](https://github.com/c3lang/c3c/commit/5e32c8a828468892f8d9ab4fb95b595c25c6e72d).\n\n## API\n\nThe `sql` package has the following API:\n\n```kotlin\n// The driver_id is registered by the respective driver module.\n// See the drivers section for how to register a custom driver.\n\ninterface Connection\n{\n\tfn void         close();\n\tfn void!        ping();\n\tfn Result!      query(String command, args...);\n\tfn usz!         exec(String command, args...);\n\tfn String       last_error();\n\n\t// Optionally, start, commit or rollback transactions\n\tfn void!        tx_begin();\n\tfn void!        tx_commit();\n\tfn void!        tx_rollback();\n\tfn bool         tx_in_progress();\n}\n\nfn Connection! open(String driver_id, String connection_string);\n\nstruct Pool {}\n\nfn Pool!        create_pool(String driver_id, String connection_string, usz max_open_conns, Duration max_idle_timeout, Allocator allocator=allocator::heap());\nfn void!        Pool.free();\nfn Connection!  Pool.acquire();\nfn void!        Pool.release(Connection conn);\nfn usz          Pool.conns_pooled();\nfn usz          Pool.conns_available();\n\n\ninterface Result\n{\n\tfn usz\t\t\tnum_columns();\n\tfn bool         next();\n\tfn void!        scan(dests...);\n\tfn void!\t\tscan_field(usz fieldnum, any dest);\n\tfn void         close();\n}\n\n\ninterface Scannable\n{\n\tfn void! \t decode(ZString raw_value);\n}\n\n\nfault Error\n{\n\tCONNECTION_FAILURE,\n\tNOT_IMPLEMENTED,\n\tCOMMAND_FAILED,\n\tPARAM_BINDING_FAILED,\n\tUNSUPPORTED_SCAN_TYPE,\n\tUNSUPPORTED_BIND_TYPE,\n\tILLEGAL_COLUMN_ACCESS,\n\tPREPARE_FAILED,\n\tUNKNOWN_ERROR,\n\tTX_ALREADY_IN_PROGRESS,\n\tPOOL_EXHAUSTED,\n}\n```\n\n### Query arguments\n\nAll supported drivers use parameter binding to safely pass arguments to queries. All types that implement the `Printable` interface are supported.\n\nThe SQL syntax to indicate parameters in a query depend on the database. For example, PostgreSQL uses `$1`, `$2` etc. For MySQL, use `?`. For SQLite, use `?`, `?N`, `:N`, `@N` or `$N` where `N` is the index of the parameter (starting at 1).\n\n### Scanning results\n\nThe following types are supported for scanning destinations:\n\n```\nString\nZString\nbool\nint int128 long short ichar\nuint uint128 ulong ushort char\ndouble\nfloat\n\nString*\nZString*\nbool*\nint* int128* long* short* ichar*\nuint* uint128* ulong* ushort* char*\ndouble*\nfloat*\n```\n\nUse the `Result.scan()` function to scan into any of these types.. If you scan into a pointer type, it will be set to `null` if the result was SQL `NULL`. Otherwise `NULL` will scan into an empty value.\n\nOther types will currently return a `UNSUPPORTED_SCAN_TYPE` fault.\n\n### Scanning into structs\n\nThe conveniece macro `Result.@scan_struct()` makes it possible to scan the whole row into a user-defined `struct`. It also provides two Tagged Attributes, that modify the scanning. Example:\n\n```kotlin\n// Define a struct\nstruct AccessKey {\n\tString group    @SqlName(\"grp\");\n\tString key      @SqlOmit;\n\tString ts       @SqlName(\"updated_at\");\n\tString name;\n\tString type;\n}\n\nfn void main()\n{\n\tsql::Connection conn = sql::open(\"postgres\", connection_string)!!;\n\tdefer try conn.close();\n\n\tString cmd = `SELECT 'vt' as grp, '1234' as key, 'now' as updated_at, 'John' as name, 'person' as type`;\n\tResult! res = conn.query(cmd)!!;\n\tdefer res.close();\n\n\tAccessKey ak;\n\tres.@scan_struct(ak)!!;\n}\n```\n\nThere are currently the following two attributes:\n\n- `@SqlName(fieldname)` is the name of the column from your query that maps to this field. The default is the name of the struct field\n- `@SqlOmit` will skip scanning into this field\n\n### Scanning into custom types\n\nOptionally, you can use your own types as scanning destinations by implementing the `decode()` function (see the `Scannable` interface). This would make it possible to implement i.e. scanning times, arrays, json fields etc.\n\nExample:\n\n```kotlin\nstruct MyScanner\n{\n\tint  int_value;\n\tbool valid;\n}\n\nfn void! MyScanner.decode(\u0026self, ZString raw_value) @dynamic\n{\n\tint tmp = raw_value.str_view().to_int()!;\n\tself.int_value = tmp;\n\tself.valid = true;\n}\n```\n\nIf the raw_value was SQL `NULL`, the `decode()` function will not be called, so your type must be laid out appropriately.\n\n## Drivers\n\nThe drivers are implemented as `sql::Driver` objects. They are registered with the `sql` module and can be used with the `sql::open` function.\n\nA driver is expected to register itself with the `sql` module using the `sql::register_driver` function:\n\n```kotlin\nfn void register_driver() @init @private {\n\tPostgres* db = mem::new(Postgres);\n\tsql::register_driver(\"postgres\", db);\n}\n```\n\nA driver must implement the `sql::Driver` interface, which contains a `free` method that will be called when the driver is unregistered on shutdown. The various driver modules provide for ample examples on how to implement a custom driver.\n\n## Project status\n\nThe library is work in progress and is still missing a lot of features. Ultimately the plan is to make it fully usable for all your SQL needs.\n\nCurrently supported:\n\n- [x] Connecting to a database\n- [x] Execution of Queries and Statements\n- [x] Scanning of result values into all native C3 types\n- [x] Scanning into arbitrary custom types\n- [x] Scanning into structs\n- [x] Parameterized queries\n- [x] Transactions\n- [x] Connection pooling\n\nIn progress:\n\n- [ ] Fix some memory leaks\n- [ ] Support binding custom types\n- [ ] Prepared statements (currently used internally, but not exposed to the user)\n- [ ] Implement some DB specific types (i.e. pq arrays)\n- [ ] Use binary encoding of params/result values\n\nOn-demand, because the currently supported databases (pq, mysql, sqlite) don't support these features:\n\n- [ ] Multiple result sets\n- [ ] Named parameters (i.e. used by MS SQL Servers)\n\n## LICENSE\n\nMIT. See [LICENSE](LICENSE).\n","funding_links":[],"categories":["Databases"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flouis77%2Fsql_c3","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flouis77%2Fsql_c3","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flouis77%2Fsql_c3/lists"}