Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/louis77/sql_c3
A C3 SQL package with support for PostgreSQL, MySQL, SQLite
https://github.com/louis77/sql_c3
c3 mysql postgresql sqlite
Last synced: 6 days ago
JSON representation
A C3 SQL package with support for PostgreSQL, MySQL, SQLite
- Host: GitHub
- URL: https://github.com/louis77/sql_c3
- Owner: louis77
- License: mit
- Created: 2024-12-23T01:57:59.000Z (19 days ago)
- Default Branch: main
- Last Pushed: 2024-12-31T11:25:08.000Z (11 days ago)
- Last Synced: 2024-12-31T12:25:51.600Z (11 days ago)
- Topics: c3, mysql, postgresql, sqlite
- Homepage:
- Size: 152 KB
- Stars: 1
- Watchers: 1
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# sql_c3
A C3 SQL package, implementing an interface, types and utilities to handle SQL connections across different database vendors in a unified way.
Database 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.
The database bindings have contributed to the [C3 vendor](https://github.com/c3lang/vendor) project. See [Installing](#installing) for instructions how to obtain them.
**This project is WIP, use at your own risk.**
- Make sure you have the latest C3 compiler installed (tested with 0.6.5)
- Clone the repository (with submodules to also get dependencies) and run the tests:```sh
git clone --recurse-submodules https://github.com/louis77/sql_c3
cd sql_c3# Get bindings for pq, mysql8 and sqlite3 from vendor library
c3c project fetch# Run tests
c3c test# Run tests with memory leak check
c3c test -D MEMCHECK
```Make sure to change the linker paths in `project.json` to point to your `libpq`, `mysql-client` or `sqlite` installation.
## Supported databases
- [X] PostgreSQL (driver_id: `postgres`) (must install `libpq`)
- [X] MySQL 8+ (driver_id: `mysql`) (must install `mysql-client`)
- [X] SQLite 3+ (driver_id: `sqlite`) (must install `sqlite3`)## Usage
This package contains the following modules:
- `sql` The user-facing interface
- `pg` Driver implementation for PostgreSQL (based on `libpq`)
- `mysql8` Driver implementation for MySQL 8.x (based on `mysql-client@8`)
- `sqlite3` Driver implementation for SQLite 3 (basen on `sqlite3`)The driver implementations are based on the bindings for the respective databases.
Generally, this is how you would use the `sql` module:
```kotlin
import sql;fn void main()
{
// Open a connection
sql::Connection conn = sql::open("postgres", "postgres://postgres@localhost/postgres")!;
defer try conn.close();// Make a simple query
String cmd = "SELECT * FROM (VALUES(1, 'hello', null), (2, 'world', null)) AS t(a_num, a_string, a_null)";Result res = conn.query(cmd)!;
defer res.close(); // Don't forget to close the result// Call next() to move to next row. Must also be called for the first row
while (res.next()) {
int a_num;
String a_string;
int* a_null; // supports checking for NULL// Scan each column into a variable.
res.scan(a_num, a_string, a_null)!;
}
}
```See the [test](test) folder for further examples of how to use the `sql` module.
## Connection Pool
There 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.
Usage:
```kotlin
// Instead of a connection, create a pool.
// You pass the maximum number of connections and the expiration
// time of unused connection.sql::Pool pool = sql::create_pool("postgres", "postgres://postgres@localhost/postgres",
16, // maximum open connections
1000 * time::MS // idle timeout
)!;
defer try pool.free();// Now you can acquire a new connection.
// If the pool is full, it throws a sql::Error.POOL_EXHAUSTED faultsql::Connection conn = pool.acquire()!;
[... do some work ...]
pool.release(conn)!;
// After a connection is released, it will be kept open
// until the idle timeout is reached
```⚠️ Due to a minor bug in C3 <= 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).
## API
The `sql` package has the following API:
```kotlin
fn Connection! open(String driver_id, String connection_string);// The driver_id is registered by the respective driver module.
// See the drivers section for how to register a custom driver.interface Connection
{
fn void close();
fn void! ping();
fn Result! query(String command, args...);
fn usz! exec(String command, args...);
fn String last_error();// Optionally, start, commit or rollback transactions
fn void! tx_begin();
fn void! tx_commit();
fn void! tx_rollback();
fn bool tx_in_progress();
}struct Pool {}
fn Pool! create_pool(String driver_id, String connection_string, usz max_open_conns, Duration max_idle_timeout, Allocator allocator=allocator::heap());
fn void! Pool.free();
fn Connection! Pool.acquire();
fn void! Pool.release(Connection conn);
fn usz Pool.conns_pooled();
fn usz Pool.conns_available();interface Result
{
fn bool next();
fn void! scan(dests...);
fn void close();
}interface Scannable
{
fn void! decode(ZString raw_value);
}fault Error
{
CONNECTION_FAILURE,
NOT_IMPLEMENTED,
COMMAND_FAILED,
PARAM_BINDING_FAILED,
UNSUPPORTED_SCAN_TYPE,
UNSUPPORTED_BIND_TYPE,
ILLEGAL_COLUMN_ACCESS,
PREPARE_FAILED,
UNKNOWN_ERROR,
TX_ALREADY_IN_PROGRESS,
POOL_EXHAUSTED,
}
```### Query arguments
All supported drivers use parameter binding to safely pass arguments to queries. All types that implement the `Printable` interface are supported.
The 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).
### Scanning results
The following types are supported for scanning destinations:
```
String
ZString
bool
int int128 long short ichar
uint uint128 ulong ushort char
double
floatString*
ZString*
bool*
int* int128* long* short* ichar*
uint* uint128* ulong* ushort* char*
double*
float*
```Use 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.
Other types will currently return a `UNSUPPORTED_SCAN_TYPE` fault.
### Scanning into custom types
Optionally, 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.
Example:
```kotlin
struct MyScanner
{
int int_value;
bool valid;
}fn void! MyScanner.decode(&self, ZString raw_value) @dynamic
{
int tmp = raw_value.str_view().to_int()!;
self.int_value = tmp;
self.valid = true;
}
```If the raw_value was SQL `NULL`, the `decode()` function will not be called, so your type must be laid out appropriately.
## Drivers
The drivers are implemented as `sql::Driver` objects. They are registered with the `sql` module and can be used with the `sql::open` function.
A driver is expected to register itself with the `sql` module using the `sql::register_driver` function:
```kotlin
fn void register_driver() @init @private {
Postgres* db = mem::new(Postgres);
sql::register_driver("postgres", db);
}
```A 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.
## Project status
The 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.
Currently supported:
- [x] Connecting to a database
- [x] Execution of Queries and Statements
- [x] Scanning of result values into all native C3 types
- [x] Parameterized queries
- [x] Transactions
- [x] Connection pooling
- [x] Support scanning into custom typeIn progress:
- [ ] Fix some memory leaks
- [ ] Support binding custom types
- [ ] Prepared statements (currently used internally, but not exposed to the user)
- [ ] Support for DB specific types
- [ ] Use binary encoding of params/result valuesOn-demand, because the currently supported databases (pq, mysql, sqlite) don't support these features:
- [ ] Multiple result sets
- [ ] Named parameters (i.e. used by MS SQL Servers)## LICENSE
MIT. See [LICENSE](LICENSE).