-
Notifications
You must be signed in to change notification settings - Fork 6
feat: SQL api #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: SQL api #29
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "rescript-bun": patch | ||
| --- | ||
|
|
||
| SQL API Bindings |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| // With PostgreSQL | ||
| let mysql = SQL.make("mysql://user:pass@localhost:3306/mydb") | ||
| let mysqlResults = await mysql->SQL.query` | ||
| SELECT * FROM users | ||
| WHERE active = ${Boolean(true)} | ||
| ` | ||
|
|
||
| // With SQLite | ||
| let sqlite = SQL.make("sqlite://myapp.db") | ||
| let sqliteResults = await sqlite->SQL.query` | ||
| SELECT * FROM users | ||
| WHERE active = ${Number(1.0)} | ||
| ` |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| let db = SQL.fromOptions({filename:":memory:", adapter:"sqlite"}) | ||
|
|
||
| (await db->SQL.query` | ||
| CREATE TABLE users ( | ||
| name TEXT NOT NULL, | ||
| email TEXT PRIMARY KEY | ||
| ); | ||
| `)->ignore | ||
|
|
||
| let insertUser = async (name, email) => await db->SQL.query` | ||
| INSERT INTO users (name, email) | ||
| VALUES (${String(name)}, ${String(email)}) | ||
| RETURNING * | ||
| ` | ||
|
|
||
| let userData = { | ||
| "name": "Alice", | ||
| "email": "alice@example.com", | ||
| } | ||
|
|
||
| let newUser = await db->SQL.query` | ||
| INSERT INTO users ${Query(db->SQL.object(userData, ["name", "email"]))} | ||
| RETURNING * | ||
| ` | ||
|
|
||
| Console.log(newUser) | ||
|
|
||
| (await insertUser("Bob", "bob@email.com"))->ignore | ||
|
|
||
| switch await db->SQL.query`SELECT * FROM users` { | ||
| | Array(users) => Console.log(users) | ||
| | _ => () | ||
| } |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,159 @@ | ||
| open Globals | ||
| module SQLQuery = { | ||
| type t<'a> = promise<'a> | ||
|
|
||
| @val external active: t<'a> => bool = "active" | ||
| @val external cancelled: t<'a> => bool = "cancelled" | ||
| @send external cancel: t<'a> => t<'a> = "cancel" | ||
| @send external simple: t<'a> => t<'a> = "simple" | ||
| @send external execute: t<'a> => t<'a> = "execute" | ||
| @send external raw: t<'a> => t<'a> = "raw" | ||
| @send external values: t<'a> => t<'a> = "values" | ||
| } | ||
|
|
||
| @unboxed | ||
| type rec param = | ||
| | Boolean(bool) | ||
| | @as(null) Null | ||
| | String(string) | ||
| | Number(float) | ||
| | Dict(Core__Dict.t<param>) | ||
| | Array(array<param>) | ||
| | Query(SQLQuery.t<param>) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can another query be a param? Interesting! |
||
|
|
||
| type t | ||
|
|
||
| @module("bun") @new | ||
| external make: string => t = "SQL" | ||
|
|
||
| module TemplateStringsArray = { | ||
| let fromArray: array<'a> => array<'a> = %raw(` | ||
| function(array) { | ||
| array.raw = [...array] | ||
| return array | ||
| } | ||
| `) | ||
| } | ||
|
|
||
| let query: (t, array<string>, array<param>) => SQLQuery.t<param> = %raw(` | ||
| function(t, strings, params) { | ||
| let templateArray = TemplateStringsArray.fromArray(strings) | ||
|
|
||
| return t(templateArray, ...params) | ||
| } | ||
| `) | ||
|
|
||
| let object: (t, {..}, array<string>) => SQLQuery.t<param> = %raw(` | ||
| function(t, obj, cols) { | ||
| return t(obj, ...cols) | ||
| } | ||
| `) | ||
|
|
||
| let values: (t, array<'a>) => SQLQuery.t<param> = %raw(` | ||
| function(t, values) { | ||
| return t(arr) | ||
| } | ||
| `) | ||
|
|
||
| @send | ||
| external commitDistributed: (t, string) => promise<unit> = "commitDistributed" | ||
|
|
||
| @send | ||
| external rollbackDistributed: (t, string) => promise<unit> = "rollbackDistributed" | ||
|
|
||
| @send | ||
| external connect: (t, unit) => promise<unit> = "connect" | ||
|
|
||
| type options = { | ||
| timeout?: float | ||
| } | ||
|
|
||
| @send | ||
| external close: (t, ~options: options=?) => promise<unit> = "close" | ||
|
|
||
| @send | ||
| external end: (t, ~options: options=?) => promise<unit> = "end" | ||
|
|
||
| @send | ||
| external flush: t => unit = "flush" | ||
|
|
||
| @send | ||
| external reserve: t => t = "reserve" | ||
|
|
||
| type sqlTransactionContextCallback<'a> = t => promise<'a> | ||
|
|
||
| @send | ||
| external begin: (t, sqlTransactionContextCallback<'b>) => promise<'a> = "begin" | ||
|
|
||
| @send | ||
| external transaction: (t, sqlTransactionContextCallback<'b>) => promise<'a> = "transaction" | ||
|
|
||
| @send | ||
| external beginDistributed: (t, string, sqlTransactionContextCallback<'b>) => promise<'a> = "beginDistributed" | ||
|
|
||
| @send | ||
| external distributed: (t, string, sqlTransactionContextCallback<'b>) => promise<'a> = "distributed" | ||
|
|
||
| @send | ||
| external unsafe: (t, string, ~values: array<JSON.t>=?) => SQLQuery.t<param> = "unsafe" | ||
|
|
||
| @send | ||
| external file: (t, string, ~values: array<JSON.t>=?) => SQLQuery.t<param> = "file" | ||
|
|
||
| type rec sqlOptions = { | ||
| filename?: string, | ||
| // Connection URL (can be string or URL object) | ||
| url?: URL.t, | ||
| // Database server hostname | ||
| host?: string, | ||
| // Database server hostname (alias for host) | ||
| hostname?: string, | ||
| // Database server port number | ||
| port?: int, | ||
| // Database user for authentication | ||
| username?: string, | ||
| // Database user for authentication (alias for username) | ||
| user?: string, | ||
| // Database password for authentication | ||
| password?: string, | ||
| // Database password for authentication (alias for password) | ||
| pass?: string, | ||
| // Name of the database to connect to | ||
| database?: string, | ||
| // Name of the database to connect to (alias for database) | ||
| db?: string, | ||
| // Database adapter/driver to use | ||
| adapter?: string, | ||
| // Maximum time in seconds to wait for connection to become available | ||
| idleTimeout?: float, | ||
| // Maximum time in seconds to wait for connection to become available (alias for idleTimeout) | ||
| idle_timeout?: float, | ||
| // Maximum time in seconds to wait when establishing a connection | ||
| connectionTimeout?: float, | ||
| // Maximum time in seconds to wait when establishing a connection (alias for connectionTimeout) | ||
| connection_timeout?: float, | ||
| // Maximum lifetime in seconds of a connection | ||
| maxLifetime?: float, | ||
| // Maximum lifetime in seconds of a connection (alias for maxLifetime) | ||
| max_lifetime?: float, | ||
| // Whether to use TLS/SSL for the connection | ||
| tls?: bool, | ||
| // Whether to use TLS/SSL for the connection (alias for tls) | ||
| ssl?: bool, | ||
| // Callback function executed when a connection is established | ||
| onconnect?: t => unit, | ||
| // Callback function executed when a connection is closed | ||
| onclose?: t => unit, | ||
| // Maximum number of connections in the pool | ||
| max?: int, | ||
| // By default values outside i32 range are returned as strings. If this is true, values outside i32 range are returned as BigInts. | ||
| bigint?: bool, | ||
| // Automatic creation of prepared statements, defaults to true | ||
| prepare?: bool, | ||
| } | ||
|
|
||
| @get | ||
| external options: t => sqlOptions = "options" | ||
|
|
||
| @module("bun") @new | ||
| external fromOptions: sqlOptions => t = "SQL" | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this primarily for autocomplete?