From daaf539092421adc15f6c3164279a3470716b560 Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Tue, 2 Jan 2024 10:59:58 -0800 Subject: [PATCH 01/12] chore: Update examples to 0.1.3 --- README.md | 20 ++++++++++---------- examples/sqlc.yaml | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index c0e6128..a76f321 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,8 @@ version: '2' plugins: - name: ts wasm: - url: https://downloads.sqlc.dev/plugin/sqlc-gen-typescript_0.1.2.wasm - sha256: f8b59cdd78b35fae157a95c5813cb09b1ebdd9a31acf2d7015465539986ccd2b + url: https://downloads.sqlc.dev/plugin/sqlc-gen-typescript_0.1.3.wasm + sha256: 287df8f6cc06377d67ad5ba02c9e0f00c585509881434d15ea8bd9fc751a9368 sql: - schema: "schema.sql" queries: "query.sql" @@ -68,8 +68,8 @@ cloud: plugins: - name: ts wasm: - url: https://downloads.sqlc.dev/plugin/sqlc-gen-typescript_0.1.1.wasm - sha256: 241c2b00bd96192eaac85f5ba435ced0d510c93f18380751c1dab3ad2fa0cfbc + url: https://downloads.sqlc.dev/plugin/sqlc-gen-typescript_0.1.3.wasm + sha256: 287df8f6cc06377d67ad5ba02c9e0f00c585509881434d15ea8bd9fc751a9368 sql: - engine: "postgresql" queries: "query.sql" @@ -255,8 +255,8 @@ version: '2' plugins: - name: ts wasm: - url: https://downloads.sqlc.dev/plugin/sqlc-gen-typescript_0.1.2.wasm - sha256: f8b59cdd78b35fae157a95c5813cb09b1ebdd9a31acf2d7015465539986ccd2b + url: https://downloads.sqlc.dev/plugin/sqlc-gen-typescript_0.1.3.wasm + sha256: 287df8f6cc06377d67ad5ba02c9e0f00c585509881434d15ea8bd9fc751a9368 sql: - schema: "schema.sql" queries: "query.sql" @@ -276,8 +276,8 @@ version: '2' plugins: - name: ts wasm: - url: https://downloads.sqlc.dev/plugin/sqlc-gen-typescript_0.1.2.wasm - sha256: f8b59cdd78b35fae157a95c5813cb09b1ebdd9a31acf2d7015465539986ccd2b + url: https://downloads.sqlc.dev/plugin/sqlc-gen-typescript_0.1.3.wasm + sha256: 287df8f6cc06377d67ad5ba02c9e0f00c585509881434d15ea8bd9fc751a9368 sql: - schema: "schema.sql" queries: "query.sql" @@ -298,8 +298,8 @@ version: '2' plugins: - name: ts wasm: - url: https://downloads.sqlc.dev/plugin/sqlc-gen-typescript_0.1.2.wasm - sha256: f8b59cdd78b35fae157a95c5813cb09b1ebdd9a31acf2d7015465539986ccd2b + url: https://downloads.sqlc.dev/plugin/sqlc-gen-typescript_0.1.3.wasm + sha256: 287df8f6cc06377d67ad5ba02c9e0f00c585509881434d15ea8bd9fc751a9368 sql: - schema: "schema.sql" queries: "query.sql" diff --git a/examples/sqlc.yaml b/examples/sqlc.yaml index 9c5de3b..e02996d 100644 --- a/examples/sqlc.yaml +++ b/examples/sqlc.yaml @@ -2,8 +2,8 @@ version: "2" plugins: - name: ts wasm: - url: https://downloads.sqlc.dev/plugin/sqlc-gen-typescript_0.1.2.wasm - sha256: f8b59cdd78b35fae157a95c5813cb09b1ebdd9a31acf2d7015465539986ccd2b + url: https://downloads.sqlc.dev/plugin/sqlc-gen-typescript_0.1.3.wasm + sha256: 287df8f6cc06377d67ad5ba02c9e0f00c585509881434d15ea8bd9fc751a9368 sql: - schema: "authors/postgresql/schema.sql" queries: "authors/postgresql/query.sql" From 31d4796f77a764fdaa6330dc6b9ef6fccf319930 Mon Sep 17 00:00:00 2001 From: Yuki Tanaka Date: Sun, 3 Mar 2024 12:06:27 +0900 Subject: [PATCH 02/12] feat: Experimental support for Node.js and `better-sqlite3` (#11) --- README.md | 21 + examples/authors/sqlite/query.sql | 18 + examples/authors/sqlite/schema.sql | 5 + .../node-better-sqlite3/package-lock.json | 486 ++++++++++++++++++ examples/node-better-sqlite3/package.json | 18 + .../node-better-sqlite3/src/db/query_sql.ts | 71 +++ examples/node-better-sqlite3/src/main.ts | 54 ++ examples/node-better-sqlite3/tsconfig.json | 109 ++++ examples/sqlc.dev.yaml | 11 +- src/app.ts | 4 + src/drivers/better-sqlite3.ts | 436 ++++++++++++++++ 11 files changed, 1232 insertions(+), 1 deletion(-) create mode 100644 examples/authors/sqlite/query.sql create mode 100644 examples/authors/sqlite/schema.sql create mode 100644 examples/node-better-sqlite3/package-lock.json create mode 100644 examples/node-better-sqlite3/package.json create mode 100644 examples/node-better-sqlite3/src/db/query_sql.ts create mode 100644 examples/node-better-sqlite3/src/main.ts create mode 100644 examples/node-better-sqlite3/tsconfig.json create mode 100644 src/drivers/better-sqlite3.ts diff --git a/README.md b/README.md index a76f321..855f342 100644 --- a/README.md +++ b/README.md @@ -311,3 +311,24 @@ sql: runtime: node driver: mysql2 # npm package name ``` + +### SQLite and better-sqlite3 (Beta) + +```yaml +version: '2' +plugins: +- name: ts + wasm: + url: https://downloads.sqlc.dev/plugin/sqlc-gen-typescript_0.1.3.wasm + sha256: 287df8f6cc06377d67ad5ba02c9e0f00c585509881434d15ea8bd9fc751a9368 +sql: +- schema: "schema.sql" + queries: "query.sql" + engine: sqlite + codegen: + - out: db + plugin: ts + options: + runtime: node + driver: better-sqlite3 # npm package name +``` diff --git a/examples/authors/sqlite/query.sql b/examples/authors/sqlite/query.sql new file mode 100644 index 0000000..25af781 --- /dev/null +++ b/examples/authors/sqlite/query.sql @@ -0,0 +1,18 @@ +-- name: GetAuthor :one +SELECT * FROM authors +WHERE id = ? LIMIT 1; + +-- name: ListAuthors :many +SELECT * FROM authors +ORDER BY name; + +-- name: CreateAuthor :exec +INSERT INTO authors ( + name, bio +) VALUES ( + ?, ? +); + +-- name: DeleteAuthor :exec +DELETE FROM authors +WHERE id = ?; diff --git a/examples/authors/sqlite/schema.sql b/examples/authors/sqlite/schema.sql new file mode 100644 index 0000000..2a44974 --- /dev/null +++ b/examples/authors/sqlite/schema.sql @@ -0,0 +1,5 @@ +CREATE TABLE authors ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name text NOT NULL, + bio text +); diff --git a/examples/node-better-sqlite3/package-lock.json b/examples/node-better-sqlite3/package-lock.json new file mode 100644 index 0000000..4c8c474 --- /dev/null +++ b/examples/node-better-sqlite3/package-lock.json @@ -0,0 +1,486 @@ +{ + "name": "node-better-sqlite3", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "node-better-sqlite3", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "better-sqlite3": "^9.4.1" + }, + "devDependencies": { + "@types/better-sqlite3": "^7.6.9", + "typescript": "^5.2.2" + } + }, + "node_modules/@types/better-sqlite3": { + "version": "7.6.9", + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.9.tgz", + "integrity": "sha512-FvktcujPDj9XKMJQWFcl2vVl7OdRIqsSRX9b0acWwTmwLK9CF2eqo/FRcmMLNpugKoX/avA6pb7TorDLmpgTnQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "20.11.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz", + "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/better-sqlite3": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-9.4.1.tgz", + "integrity": "sha512-QpqiQeMI4WkE+dQ68zTMX5OzlPGc7lXIDP1iKUt4Omt9PdaVgzKYxHIJRIzt1E+RUBQoFmkip/IbvzyrxehAIg==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" + }, + "node_modules/node-abi": { + "version": "3.54.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.54.0.tgz", + "integrity": "sha512-p7eGEiQil0YUV3ItH4/tBb781L5impVmmx2E9FRKF7d18XXzp4PGT2tdYMFY6wQqgxD0IwNZOiSJ0/K0fSi/OA==", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } +} diff --git a/examples/node-better-sqlite3/package.json b/examples/node-better-sqlite3/package.json new file mode 100644 index 0000000..02c9906 --- /dev/null +++ b/examples/node-better-sqlite3/package.json @@ -0,0 +1,18 @@ +{ + "name": "node-better-sqlite3", + "version": "1.0.0", + "description": "", + "main": "src/main.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@types/better-sqlite3": "^7.6.9", + "typescript": "^5.2.2" + }, + "dependencies": { + "better-sqlite3": "^9.4.1" + } +} diff --git a/examples/node-better-sqlite3/src/db/query_sql.ts b/examples/node-better-sqlite3/src/db/query_sql.ts new file mode 100644 index 0000000..04583f3 --- /dev/null +++ b/examples/node-better-sqlite3/src/db/query_sql.ts @@ -0,0 +1,71 @@ +import { Database } from "better-sqlite3"; + +export const getAuthorQuery = `-- name: GetAuthor :one +SELECT id, name, bio FROM authors +WHERE id = ? LIMIT 1`; + +export interface GetAuthorArgs { + id: any; +} + +export interface GetAuthorRow { + id: any; + name: any; + bio: any | null; +} + +export async function getAuthor(database: Database, args: GetAuthorArgs): Promise { + const stmt = database.prepare(getAuthorQuery); + const result = await stmt.get(args.id); + if (result == undefined) { + return null; + } + return result as GetAuthorRow; +} + +export const listAuthorsQuery = `-- name: ListAuthors :many +SELECT id, name, bio FROM authors +ORDER BY name`; + +export interface ListAuthorsRow { + id: any; + name: any; + bio: any | null; +} + +export async function listAuthors(database: Database): Promise { + const stmt = database.prepare(listAuthorsQuery); + const result = await stmt.all(); + return result as ListAuthorsRow[]; +} + +export const createAuthorQuery = `-- name: CreateAuthor :exec +INSERT INTO authors ( + name, bio +) VALUES ( + ?, ? +)`; + +export interface CreateAuthorArgs { + name: any; + bio: any | null; +} + +export async function createAuthor(database: Database, args: CreateAuthorArgs): Promise { + const stmt = database.prepare(createAuthorQuery); + await stmt.run(args.name, args.bio); +} + +export const deleteAuthorQuery = `-- name: DeleteAuthor :exec +DELETE FROM authors +WHERE id = ?`; + +export interface DeleteAuthorArgs { + id: any; +} + +export async function deleteAuthor(database: Database, args: DeleteAuthorArgs): Promise { + const stmt = database.prepare(deleteAuthorQuery); + await stmt.run(args.id); +} + diff --git a/examples/node-better-sqlite3/src/main.ts b/examples/node-better-sqlite3/src/main.ts new file mode 100644 index 0000000..5ee0ee4 --- /dev/null +++ b/examples/node-better-sqlite3/src/main.ts @@ -0,0 +1,54 @@ +import Database from "better-sqlite3"; +import { readFile } from "node:fs/promises"; +import { join } from "node:path"; + +import { + createAuthor, + deleteAuthor, + getAuthor, + listAuthors, +} from "./db/query_sql"; + +interface Author { + id: string; + name: string; + bio: string | null; +} + +async function main() { + const ddl = await readFile(join(__dirname, "../../authors/sqlite/schema.sql"), { encoding: 'utf-8' }); + const database = new Database(":memory:"); + + // Create tables + database.exec(ddl); + + // Create an author + await createAuthor(database, { + name: "Seal", + bio: "Kissed from a rose", + }); + + // List the authors + const authors = await listAuthors(database); + console.log(authors); + + // Get that author + const seal = await getAuthor(database, { id: authors[0].id }); + if (seal === null) { + throw new Error("seal not found"); + } + console.log(seal); + + // Delete the author + await deleteAuthor(database, { id: seal.id }); +} + +(async () => { + try { + await main(); + process.exit(0); + } catch (e) { + console.error(e); + process.exit(1); + } +})(); diff --git a/examples/node-better-sqlite3/tsconfig.json b/examples/node-better-sqlite3/tsconfig.json new file mode 100644 index 0000000..3466896 --- /dev/null +++ b/examples/node-better-sqlite3/tsconfig.json @@ -0,0 +1,109 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git a/examples/sqlc.dev.yaml b/examples/sqlc.dev.yaml index df52a0a..d2a69a0 100644 --- a/examples/sqlc.dev.yaml +++ b/examples/sqlc.dev.yaml @@ -57,4 +57,13 @@ sql: out: bun-mysql2/src/db options: runtime: bun - driver: mysql2 \ No newline at end of file + driver: mysql2 +- schema: "authors/sqlite/schema.sql" + queries: "authors/sqlite/query.sql" + engine: "sqlite" + codegen: + - plugin: ts + out: node-better-sqlite3/src/db + options: + runtime: node + driver: better-sqlite3 diff --git a/src/app.ts b/src/app.ts index 4006c39..110472c 100644 --- a/src/app.ts +++ b/src/app.ts @@ -28,6 +28,7 @@ import { } from "./gen/plugin/codegen_pb"; import { argName, colName } from "./drivers/utlis"; +import betterSQLite3 from "./drivers/better-sqlite3"; import pg from "./drivers/pg"; import postgres from "./drivers/postgres"; import mysql2 from "./drivers/mysql2"; @@ -82,6 +83,9 @@ function createNodeGenerator(driver?: string): Driver { case "postgres": { return postgres; } + case "better-sqlite3": { + return betterSQLite3; + } } throw new Error(`unknown driver: ${driver}`); } diff --git a/src/drivers/better-sqlite3.ts b/src/drivers/better-sqlite3.ts new file mode 100644 index 0000000..7e2f9c3 --- /dev/null +++ b/src/drivers/better-sqlite3.ts @@ -0,0 +1,436 @@ +import { SyntaxKind, NodeFlags, Node, TypeNode, factory } from "typescript"; + +import { Parameter, Column, Query } from "../gen/plugin/codegen_pb"; +import { argName } from "./utlis"; + +/** + * {@link https://github.com/WiseLibs/better-sqlite3/blob/v9.4.1/docs/api.md#binding-parameters} + * {@link https://github.com/sqlc-dev/sqlc/blob/v1.25.0/internal/codegen/golang/sqlite_type.go} + */ +export function columnType(column?: Column): TypeNode { + if (column === undefined || column.type === undefined) { + return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); + } + + let typ: TypeNode = factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); + switch (column.type.name) { + case "int": + case "integer": + case "tinyint": + case "smallint": + case "mediumint": + case "bigint": + case "unsignedbigint": + case "int2": + case "int8": { + // TODO: Improve `BigInt` handling (https://github.com/WiseLibs/better-sqlite3/blob/v9.4.1/docs/integer.md) + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "blob": { + // TODO: Is this correct or node-specific? + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Buffer"), + undefined + ); + break; + } + case "real": + case "double": + case "doubleprecision": + case "float": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "boolean": + case "bool": { + typ = factory.createKeywordTypeNode(SyntaxKind.BooleanKeyword); + break; + } + case "date": + case "datetime": + case "timestamp": { + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Date"), + undefined + ); + break; + } + } + + if (column.notNull) { + return typ; + } + + return factory.createUnionTypeNode([ + typ, + factory.createLiteralTypeNode(factory.createNull()), + ]); +} + +export function preamble(queries: Query[]) { + const imports: Node[] = [ + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([ + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("Database") + ), + ]) + ), + factory.createStringLiteral("better-sqlite3"), + undefined + ), + ]; + + return imports; +} + +function funcParamsDecl(iface: string | undefined, params: Parameter[]) { + let funcParams = [ + factory.createParameterDeclaration( + undefined, + undefined, + factory.createIdentifier("database"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("Database"), + undefined + ), + undefined + ), + ]; + + if (iface && params.length > 0) { + funcParams.push( + factory.createParameterDeclaration( + undefined, + undefined, + factory.createIdentifier("args"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier(iface), + undefined + ), + undefined + ) + ); + } + + return funcParams; +} + +export function execDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + params: Parameter[] +) { + const funcParams = funcParamsDecl(argIface, params); + + return factory.createFunctionDeclaration( + [ + factory.createToken(SyntaxKind.ExportKeyword), + factory.createToken(SyntaxKind.AsyncKeyword), + ], + undefined, + factory.createIdentifier(funcName), + undefined, + funcParams, + factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ + factory.createKeywordTypeNode(SyntaxKind.VoidKeyword), + ]), + factory.createBlock( + [ + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createIdentifier("stmt"), + undefined, + undefined, + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("database"), + factory.createIdentifier("prepare") + ), + undefined, + [ + factory.createIdentifier(queryName) + ] + ) + ), + ], + NodeFlags.Const | + // ts.NodeFlags.Constant | + // NodeFlags.AwaitContext | + // ts.NodeFlags.Constant | + // NodeFlags.ContextFlags | + NodeFlags.TypeExcludesFlags + ) + ), + factory.createExpressionStatement( + factory.createAwaitExpression( + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("stmt"), + factory.createIdentifier("run") + ), + undefined, + params.map((param, i) => + factory.createPropertyAccessExpression( + factory.createIdentifier("args"), + factory.createIdentifier(argName(i, param.column)) + ) + ), + ) + ) + ), + ], + true + ) + ); +} + +export function oneDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + returnIface: string, + params: Parameter[], + columns: Column[] +) { + const funcParams = funcParamsDecl(argIface, params); + + return factory.createFunctionDeclaration( + [ + factory.createToken(SyntaxKind.ExportKeyword), + factory.createToken(SyntaxKind.AsyncKeyword), + ], + undefined, + factory.createIdentifier(funcName), + undefined, + funcParams, + factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ + factory.createUnionTypeNode([ + factory.createTypeReferenceNode( + factory.createIdentifier(returnIface), + undefined + ), + factory.createLiteralTypeNode(factory.createNull()), + ]), + ]), + factory.createBlock( + [ + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createIdentifier("stmt"), + undefined, + undefined, + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("database"), + factory.createIdentifier("prepare") + ), + undefined, + [ + factory.createIdentifier(queryName) + ] + ) + ), + ], + NodeFlags.Const | + // ts.NodeFlags.Constant | + // NodeFlags.AwaitContext | + // ts.NodeFlags.Constant | + // NodeFlags.ContextFlags | + NodeFlags.TypeExcludesFlags + ) + ), + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createIdentifier("result"), + undefined, + undefined, + factory.createAwaitExpression( + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("stmt"), + factory.createIdentifier("get") + ), + undefined, + params.map((param, i) => + factory.createPropertyAccessExpression( + factory.createIdentifier("args"), + factory.createIdentifier( + argName(i, param.column) + ) + ) + ), + ) + ) + ), + ], + NodeFlags.Const | + // ts.NodeFlags.Constant | + NodeFlags.AwaitContext | + // ts.NodeFlags.Constant | + NodeFlags.ContextFlags | + NodeFlags.TypeExcludesFlags + ) + ), + factory.createIfStatement( + factory.createBinaryExpression( + factory.createIdentifier("result"), + factory.createToken(SyntaxKind.EqualsEqualsToken), + factory.createIdentifier("undefined") + ), + factory.createBlock( + [factory.createReturnStatement(factory.createNull())], + true + ), + undefined + ), + factory.createReturnStatement( + factory.createAsExpression( + factory.createIdentifier("result"), + factory.createTypeReferenceNode( + factory.createIdentifier(returnIface), + undefined + ) + ) + ), + ], + true + ) + ); +} + +export function manyDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + returnIface: string, + params: Parameter[], + columns: Column[] +) { + const funcParams = funcParamsDecl(argIface, params); + + return factory.createFunctionDeclaration( + [ + factory.createToken(SyntaxKind.ExportKeyword), + factory.createToken(SyntaxKind.AsyncKeyword), + ], + undefined, + factory.createIdentifier(funcName), + undefined, + funcParams, + factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ + factory.createArrayTypeNode( + factory.createTypeReferenceNode( + factory.createIdentifier(returnIface), + undefined + ) + ), + ]), + factory.createBlock( + [ + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createIdentifier("stmt"), + undefined, + undefined, + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("database"), + factory.createIdentifier("prepare") + ), + undefined, + [ + factory.createIdentifier(queryName) + ] + ) + ), + ], + NodeFlags.Const | + // ts.NodeFlags.Constant | + // NodeFlags.AwaitContext | + // ts.NodeFlags.Constant | + // NodeFlags.ContextFlags | + NodeFlags.TypeExcludesFlags + ) + ), + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createIdentifier("result"), + undefined, + undefined, + factory.createAwaitExpression( + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("stmt"), + factory.createIdentifier("all") + ), + undefined, + params.map((param, i) => + factory.createPropertyAccessExpression( + factory.createIdentifier("args"), + factory.createIdentifier( + argName(i, param.column) + ) + ) + ), + ) + ) + ), + ], + NodeFlags.Const | + // NodeFlags.Constant | + NodeFlags.AwaitContext | + // NodeFlags.Constant | + NodeFlags.ContextFlags | + NodeFlags.TypeExcludesFlags + ) + ), + factory.createReturnStatement( + factory.createAsExpression( + factory.createIdentifier("result"), + factory.createArrayTypeNode( + factory.createTypeReferenceNode( + factory.createIdentifier(returnIface), + undefined + ) + ) + ) + ), + ], + true + ) + ); +} + +export default { + columnType, + execDecl, + manyDecl, + oneDecl, + preamble, +}; From cb1253fb5da61fa67e9e3f5f8c5a01d4d4266ffb Mon Sep 17 00:00:00 2001 From: Kyle Gray Date: Sat, 2 Mar 2024 20:15:18 -0800 Subject: [PATCH 03/12] chore: Use local databases (#19) Use two new actions to run PostgreSQL and MySQL inside GitHub actions itself. --- .github/workflows/examples.yml | 81 ++++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 9 deletions(-) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 9c6084e..6defb1a 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -1,13 +1,38 @@ name: examples on: [push] jobs: - bun: + bun-mysql: runs-on: ubuntu-latest strategy: matrix: include: - dir: "examples/bun-mysql2" qs: "mysql" + steps: + - uses: actions/checkout@v4 + - uses: sqlc-dev/setup-sqlc@v4 + with: + sqlc-version: '1.24.0' + - uses: oven-sh/setup-bun@v1 + with: + bun-version: latest + - uses: shogo82148/actions-setup-mysql@v1 + with: + mysql-version: "8.1" + - run: bun install + working-directory: ${{ matrix.dir }} + - run: mysql --user="root" --database="mysql" < schema.sql + working-directory: examples/authors/mysql + - run: bun run src/main.ts + working-directory: ${{ matrix.dir }} + env: + DATABASE_URL: mysql://root:@localhost:3306/mysql + + bun-postgresql: + runs-on: ubuntu-latest + strategy: + matrix: + include: - dir: "examples/bun-postgres" qs: "postgresql" - dir: "examples/bun-pg" @@ -20,21 +45,53 @@ jobs: - uses: oven-sh/setup-bun@v1 with: bun-version: latest + - uses: sqlc-dev/action-setup-postgres@master + with: + postgres-version: "16" + id: postgres - run: bun install working-directory: ${{ matrix.dir }} - - run: echo "DATABASE_URL=$(sqlc createdb --queryset ${{ matrix.qs }})" >> $GITHUB_ENV - working-directory: examples/authors + - run: psql -f schema.sql "$DATABASE_URL" + working-directory: examples/authors/postgresql env: - SQLC_AUTH_TOKEN: ${{ secrets.SQLC_AUTH_TOKEN }} + DATABASE_URL: ${{ steps.postgres.outputs.connection-uri }}?sslmode=disable - run: bun run src/main.ts working-directory: ${{ matrix.dir }} - node: + env: + DATABASE_URL: ${{ steps.postgres.outputs.connection-uri }}?sslmode=disable + + node-mysql: runs-on: ubuntu-latest strategy: matrix: include: - dir: "examples/node-mysql2" qs: "mysql" + steps: + - uses: actions/checkout@v4 + - uses: sqlc-dev/setup-sqlc@v4 + with: + sqlc-version: '1.24.0' + - uses: actions/setup-node@v4 + - uses: shogo82148/actions-setup-mysql@v1 + with: + mysql-version: "8.1" + - run: npm install + working-directory: ${{ matrix.dir }} + - run: npx tsc + working-directory: ${{ matrix.dir }} + - run: mysql --user="root" --database="mysql" < schema.sql + working-directory: examples/authors/mysql + - run: node ./src/main.js + working-directory: ${{ matrix.dir }} + env: + DATABASE_URL: mysql://root:@localhost:3306/mysql + + node-postgresql: + runs-on: ubuntu-latest + strategy: + matrix: + include: - dir: "examples/node-postgres" qs: "postgresql" - dir: "examples/node-pg" @@ -45,13 +102,19 @@ jobs: with: sqlc-version: '1.24.0' - uses: actions/setup-node@v4 + - uses: sqlc-dev/action-setup-postgres@master + with: + postgres-version: "16" + id: postgres - run: npm install working-directory: ${{ matrix.dir }} - run: npx tsc working-directory: ${{ matrix.dir }} - - run: echo "DATABASE_URL=$(sqlc createdb --queryset ${{ matrix.qs }})" >> $GITHUB_ENV - working-directory: examples/authors + - run: psql -f schema.sql "$DATABASE_URL" + working-directory: examples/authors/postgresql env: - SQLC_AUTH_TOKEN: ${{ secrets.SQLC_AUTH_TOKEN }} + DATABASE_URL: ${{ steps.postgres.outputs.connection-uri }}?sslmode=disable - run: node ./src/main.js - working-directory: ${{ matrix.dir }} \ No newline at end of file + working-directory: ${{ matrix.dir }} + env: + DATABASE_URL: ${{ steps.postgres.outputs.connection-uri }}?sslmode=disable \ No newline at end of file From 27833bebb88b9a39b817b6fb27dbf672f9f8e458 Mon Sep 17 00:00:00 2001 From: Kyle Gray Date: Sat, 2 Mar 2024 20:16:54 -0800 Subject: [PATCH 04/12] feat: Add DO NOT EDIT comment to files (#17) * feat: Add DO NOT EDIT comment to files * Update examples --- examples/bun-mysql2/src/db/query_sql.ts | 2 ++ examples/bun-pg/src/db/query_sql.ts | 2 ++ examples/bun-postgres/src/db/query_sql.ts | 2 ++ examples/node-better-sqlite3/src/db/query_sql.ts | 2 ++ examples/node-mysql2/src/db/query_sql.ts | 2 ++ examples/node-pg/src/db/query_sql.ts | 2 ++ examples/node-postgres/src/db/query_sql.ts | 2 ++ src/app.ts | 2 +- 8 files changed, 15 insertions(+), 1 deletion(-) diff --git a/examples/bun-mysql2/src/db/query_sql.ts b/examples/bun-mysql2/src/db/query_sql.ts index 9e70d09..475f7bf 100644 --- a/examples/bun-mysql2/src/db/query_sql.ts +++ b/examples/bun-mysql2/src/db/query_sql.ts @@ -1,3 +1,5 @@ +// Code generated by sqlc. DO NOT EDIT. + import mysql, { RowDataPacket } from "mysql2/promise"; type Client = mysql.Connection | mysql.Pool; diff --git a/examples/bun-pg/src/db/query_sql.ts b/examples/bun-pg/src/db/query_sql.ts index ffae8a9..2e8db52 100644 --- a/examples/bun-pg/src/db/query_sql.ts +++ b/examples/bun-pg/src/db/query_sql.ts @@ -1,3 +1,5 @@ +// Code generated by sqlc. DO NOT EDIT. + import { QueryArrayConfig, QueryArrayResult } from "pg"; interface Client { diff --git a/examples/bun-postgres/src/db/query_sql.ts b/examples/bun-postgres/src/db/query_sql.ts index fc99497..a67e2b6 100644 --- a/examples/bun-postgres/src/db/query_sql.ts +++ b/examples/bun-postgres/src/db/query_sql.ts @@ -1,3 +1,5 @@ +// Code generated by sqlc. DO NOT EDIT. + import { Sql } from "postgres"; export const getAuthorQuery = `-- name: GetAuthor :one diff --git a/examples/node-better-sqlite3/src/db/query_sql.ts b/examples/node-better-sqlite3/src/db/query_sql.ts index 04583f3..8996855 100644 --- a/examples/node-better-sqlite3/src/db/query_sql.ts +++ b/examples/node-better-sqlite3/src/db/query_sql.ts @@ -1,3 +1,5 @@ +// Code generated by sqlc. DO NOT EDIT. + import { Database } from "better-sqlite3"; export const getAuthorQuery = `-- name: GetAuthor :one diff --git a/examples/node-mysql2/src/db/query_sql.ts b/examples/node-mysql2/src/db/query_sql.ts index 9e70d09..475f7bf 100644 --- a/examples/node-mysql2/src/db/query_sql.ts +++ b/examples/node-mysql2/src/db/query_sql.ts @@ -1,3 +1,5 @@ +// Code generated by sqlc. DO NOT EDIT. + import mysql, { RowDataPacket } from "mysql2/promise"; type Client = mysql.Connection | mysql.Pool; diff --git a/examples/node-pg/src/db/query_sql.ts b/examples/node-pg/src/db/query_sql.ts index ffae8a9..2e8db52 100644 --- a/examples/node-pg/src/db/query_sql.ts +++ b/examples/node-pg/src/db/query_sql.ts @@ -1,3 +1,5 @@ +// Code generated by sqlc. DO NOT EDIT. + import { QueryArrayConfig, QueryArrayResult } from "pg"; interface Client { diff --git a/examples/node-postgres/src/db/query_sql.ts b/examples/node-postgres/src/db/query_sql.ts index fc99497..a67e2b6 100644 --- a/examples/node-postgres/src/db/query_sql.ts +++ b/examples/node-postgres/src/db/query_sql.ts @@ -1,3 +1,5 @@ +// Code generated by sqlc. DO NOT EDIT. + import { Sql } from "postgres"; export const getAuthorQuery = `-- name: GetAuthor :one diff --git a/src/app.ts b/src/app.ts index 110472c..7f8a9e6 100644 --- a/src/app.ts +++ b/src/app.ts @@ -278,7 +278,7 @@ function printNode(nodes: Node[]): string { ScriptKind.TS ); const printer = createPrinter({ newLine: NewLineKind.LineFeed }); - let output = ""; + let output = "// Code generated by sqlc. DO NOT EDIT.\n\n"; for (let node of nodes) { output += printer.printNode(EmitHint.Unspecified, node, resultFile); output += "\n\n"; From 0134a9571261c4f74471d209ed656f46887e79ba Mon Sep 17 00:00:00 2001 From: Kyle Gray Date: Sat, 2 Mar 2024 20:17:23 -0800 Subject: [PATCH 05/12] fix: Check for empty row (#18) Fixes a compiler error. Fixes #6 --- examples/bun-postgres/src/db/query_sql.ts | 6 +++ examples/node-postgres/src/db/query_sql.ts | 6 +++ src/drivers/postgres.ts | 49 ++++++++++++++++------ 3 files changed, 49 insertions(+), 12 deletions(-) diff --git a/examples/bun-postgres/src/db/query_sql.ts b/examples/bun-postgres/src/db/query_sql.ts index a67e2b6..8e15f3b 100644 --- a/examples/bun-postgres/src/db/query_sql.ts +++ b/examples/bun-postgres/src/db/query_sql.ts @@ -22,6 +22,9 @@ export async function getAuthor(sql: Sql, args: GetAuthorArgs): Promise factory.createPropertyAccessExpression( factory.createIdentifier("args"), - factory.createIdentifier(argName(i, param.column)) + factory.createIdentifier( + argName(i, param.column) + ) ) ), false @@ -520,7 +526,7 @@ export function oneDecl( factory.createIdentifier("values") ), undefined, - undefined, + undefined ) ) ), @@ -550,12 +556,31 @@ export function oneDecl( ), factory.createVariableStatement( undefined, - factory.createVariableDeclarationList([ - factory.createVariableDeclaration("row", undefined, undefined, factory.createElementAccessExpression( - factory.createIdentifier("rows"), - factory.createNumericLiteral("0") - )), - ], NodeFlags.Const) + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + "row", + undefined, + undefined, + factory.createElementAccessExpression( + factory.createIdentifier("rows"), + factory.createNumericLiteral("0") + ) + ), + ], + NodeFlags.Const + ) + ), + factory.createIfStatement( + factory.createPrefixUnaryExpression( + SyntaxKind.ExclamationToken, + factory.createIdentifier("row") + ), + factory.createBlock( + [factory.createReturnStatement(factory.createNull())], + true + ), + undefined ), factory.createReturnStatement( factory.createObjectLiteralExpression( From b79c5fa74da696f4180df419fbb545bf967f7619 Mon Sep 17 00:00:00 2001 From: Yasuhiro SHIMIZU Date: Sun, 3 Mar 2024 13:33:56 +0900 Subject: [PATCH 06/12] feat(mysql): Add :execlastid implementation for mysql (#13) * add execlastid implementation for mysql * pg and postgress driver should throw on unsupported execlastid command * only import ResultSetHeader if execlastid query exists * add throwing execlastidDecl to better-sqlite3 driver * update examples --- Makefile | 2 +- examples/authors/mysql/query.sql | 11 ++- examples/bun-mysql2/src/db/query_sql.ts | 24 ++++- examples/node-mysql2/src/db/query_sql.ts | 24 ++++- src/app.ts | 12 +++ src/drivers/better-sqlite3.ts | 12 ++- src/drivers/mysql2.ts | 118 ++++++++++++++++++++++- src/drivers/pg.ts | 12 ++- src/drivers/postgres.ts | 12 ++- 9 files changed, 215 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 45c89dc..8ab6195 100644 --- a/Makefile +++ b/Makefile @@ -12,4 +12,4 @@ out.js: src/app.ts $(wildcard src/drivers/*.ts) src/gen/plugin/codegen_pb.ts npx esbuild --bundle src/app.ts --tree-shaking=true --format=esm --target=es2020 --outfile=out.js src/gen/plugin/codegen_pb.ts: buf.gen.yaml - buf generate --template buf.gen.yaml buf.build/sqlc/sqlc --path plugin/ \ No newline at end of file + buf generate --template buf.gen.yaml buf.build/sqlc/sqlc --path plugin/ diff --git a/examples/authors/mysql/query.sql b/examples/authors/mysql/query.sql index 158c8e5..94c51b9 100644 --- a/examples/authors/mysql/query.sql +++ b/examples/authors/mysql/query.sql @@ -10,7 +10,14 @@ ORDER BY name; INSERT INTO authors ( name, bio ) VALUES ( - ?, ? + ?, ? +); + +/* name: CreateAuthorReturnId :execlastid */ +INSERT INTO authors ( + name, bio +) VALUES ( + ?, ? ); /* name: DeleteAuthor :exec */ @@ -19,4 +26,4 @@ WHERE id = ?; /* name: Test :one */ SELECT * FROM node_mysql_types -LIMIT 1; \ No newline at end of file +LIMIT 1; diff --git a/examples/bun-mysql2/src/db/query_sql.ts b/examples/bun-mysql2/src/db/query_sql.ts index 475f7bf..1673007 100644 --- a/examples/bun-mysql2/src/db/query_sql.ts +++ b/examples/bun-mysql2/src/db/query_sql.ts @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. -import mysql, { RowDataPacket } from "mysql2/promise"; +import mysql, { RowDataPacket, ResultSetHeader } from "mysql2/promise"; type Client = mysql.Connection | mysql.Pool; @@ -64,7 +64,7 @@ export const createAuthorQuery = `-- name: CreateAuthor :exec INSERT INTO authors ( name, bio ) VALUES ( - ?, ? + ?, ? )`; export interface CreateAuthorArgs { @@ -79,6 +79,26 @@ export async function createAuthor(client: Client, args: CreateAuthorArgs): Prom }); } +export const createAuthorReturnIdQuery = `-- name: CreateAuthorReturnId :execlastid +INSERT INTO authors ( + name, bio +) VALUES ( + ?, ? +)`; + +export interface CreateAuthorReturnIdArgs { + name: string; + bio: string | null; +} + +export async function createAuthorReturnId(client: Client, args: CreateAuthorReturnIdArgs): Promise { + const [result] = await client.query({ + sql: createAuthorReturnIdQuery, + values: [args.name, args.bio] + }); + return result?.insertId ?? 0; +} + export const deleteAuthorQuery = `-- name: DeleteAuthor :exec DELETE FROM authors WHERE id = ?`; diff --git a/examples/node-mysql2/src/db/query_sql.ts b/examples/node-mysql2/src/db/query_sql.ts index 475f7bf..1673007 100644 --- a/examples/node-mysql2/src/db/query_sql.ts +++ b/examples/node-mysql2/src/db/query_sql.ts @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. -import mysql, { RowDataPacket } from "mysql2/promise"; +import mysql, { RowDataPacket, ResultSetHeader } from "mysql2/promise"; type Client = mysql.Connection | mysql.Pool; @@ -64,7 +64,7 @@ export const createAuthorQuery = `-- name: CreateAuthor :exec INSERT INTO authors ( name, bio ) VALUES ( - ?, ? + ?, ? )`; export interface CreateAuthorArgs { @@ -79,6 +79,26 @@ export async function createAuthor(client: Client, args: CreateAuthorArgs): Prom }); } +export const createAuthorReturnIdQuery = `-- name: CreateAuthorReturnId :execlastid +INSERT INTO authors ( + name, bio +) VALUES ( + ?, ? +)`; + +export interface CreateAuthorReturnIdArgs { + name: string; + bio: string | null; +} + +export async function createAuthorReturnId(client: Client, args: CreateAuthorReturnIdArgs): Promise { + const [result] = await client.query({ + sql: createAuthorReturnIdQuery, + values: [args.name, args.bio] + }); + return result?.insertId ?? 0; +} + export const deleteAuthorQuery = `-- name: DeleteAuthor :exec DELETE FROM authors WHERE id = ?`; diff --git a/src/app.ts b/src/app.ts index 7f8a9e6..36e1ddd 100644 --- a/src/app.ts +++ b/src/app.ts @@ -54,6 +54,12 @@ interface Driver { iface: string | undefined, params: Parameter[] ) => Node; + execlastidDecl: ( + name: string, + text: string, + iface: string | undefined, + params: Parameter[] + ) => Node; manyDecl: ( name: string, text: string, @@ -160,6 +166,12 @@ ${query.text}` ); break; } + case ":execlastid": { + nodes.push( + driver.execlastidDecl(lowerName, textName, argIface, query.params) + ) + break; + } case ":one": { nodes.push( driver.oneDecl( diff --git a/src/drivers/better-sqlite3.ts b/src/drivers/better-sqlite3.ts index 7e2f9c3..8665b91 100644 --- a/src/drivers/better-sqlite3.ts +++ b/src/drivers/better-sqlite3.ts @@ -1,4 +1,4 @@ -import { SyntaxKind, NodeFlags, Node, TypeNode, factory } from "typescript"; +import { SyntaxKind, NodeFlags, Node, TypeNode, factory, FunctionDeclaration } from "typescript"; import { Parameter, Column, Query } from "../gen/plugin/codegen_pb"; import { argName } from "./utlis"; @@ -427,10 +427,20 @@ export function manyDecl( ); } +export function execlastidDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + params: Parameter[] +): FunctionDeclaration { + throw new Error('better-sqlite3 driver currently does not support :execlastid') +} + export default { columnType, execDecl, manyDecl, oneDecl, preamble, + execlastidDecl, }; diff --git a/src/drivers/mysql2.ts b/src/drivers/mysql2.ts index d6f943b..bc052bb 100644 --- a/src/drivers/mysql2.ts +++ b/src/drivers/mysql2.ts @@ -2,7 +2,7 @@ import { SyntaxKind, NodeFlags, TypeNode, factory } from "typescript"; // import { writeFileSync, STDIO } from "javy/fs"; -import { Parameter, Column } from "../gen/plugin/codegen_pb"; +import { Parameter, Column, Query } from "../gen/plugin/codegen_pb"; import { argName, colName } from "./utlis"; export function columnType(column?: Column): TypeNode { @@ -165,7 +165,8 @@ export function columnType(column?: Column): TypeNode { ]); } -export function preamble(queries: unknown) { +export function preamble(queries: Query[]) { + const hasExecLastIdCmd = queries.some((query) => query.cmd === ":execlastid"); return [ factory.createImportDeclaration( undefined, @@ -178,6 +179,15 @@ export function preamble(queries: unknown) { undefined, factory.createIdentifier("RowDataPacket") ), + ...(hasExecLastIdCmd + ? [ + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("ResultSetHeader") + ), + ] + : []), ]) ), factory.createStringLiteral("mysql2/promise"), @@ -611,10 +621,114 @@ export function oneDecl( ); } +export function execlastidDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + params: Parameter[] +) { + const funcParams = funcParamsDecl(argIface, params); + + return factory.createFunctionDeclaration( + [ + factory.createToken(SyntaxKind.ExportKeyword), + factory.createToken(SyntaxKind.AsyncKeyword), + ], + undefined, + factory.createIdentifier(funcName), + undefined, + funcParams, + factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ + factory.createTypeReferenceNode("number", undefined), + ]), + factory.createBlock( + [ + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createArrayBindingPattern([ + factory.createBindingElement( + undefined, + undefined, + factory.createIdentifier("result"), + undefined + ), + ]), + undefined, + undefined, + factory.createAwaitExpression( + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("client"), + factory.createIdentifier("query") + ), + [ + factory.createTypeReferenceNode( + factory.createIdentifier("ResultSetHeader"), + undefined + ), + ], + [ + factory.createObjectLiteralExpression( + [ + factory.createPropertyAssignment( + factory.createIdentifier("sql"), + factory.createIdentifier(queryName) + ), + factory.createPropertyAssignment( + factory.createIdentifier("values"), + factory.createArrayLiteralExpression( + params.map((param, i) => + factory.createPropertyAccessExpression( + factory.createIdentifier("args"), + factory.createIdentifier( + argName(i, param.column) + ) + ) + ), + false + ) + ), + ], + true + ), + ] + ) + ) + ), + ], + NodeFlags.Const | + // NodeFlags.Constant | + NodeFlags.AwaitContext | + // NodeFlags.Constant | + NodeFlags.ContextFlags | + NodeFlags.TypeExcludesFlags + ) + ), + factory.createReturnStatement( + factory.createBinaryExpression( + factory.createPropertyAccessChain( + factory.createIdentifier("result"), + factory.createToken(SyntaxKind.QuestionDotToken), + factory.createIdentifier("insertId") + ), + factory.createToken(SyntaxKind.QuestionQuestionToken), + factory.createNumericLiteral(0) + ) + ), + ], + true + ) + ); +} + export default { columnType, preamble, execDecl, manyDecl, oneDecl, + execlastidDecl, }; diff --git a/src/drivers/pg.ts b/src/drivers/pg.ts index db6936d..9272035 100644 --- a/src/drivers/pg.ts +++ b/src/drivers/pg.ts @@ -1,4 +1,4 @@ -import { SyntaxKind, NodeFlags, Node, TypeNode, factory } from "typescript"; +import { SyntaxKind, NodeFlags, Node, TypeNode, factory, FunctionDeclaration } from "typescript"; import { Parameter, Column, Query } from "../gen/plugin/codegen_pb"; import { argName, colName } from "./utlis"; @@ -778,10 +778,20 @@ export function manyDecl( ); } +export function execlastidDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + params: Parameter[] +): FunctionDeclaration { + throw new Error('pg driver currently does not support :execlastid') +} + export default { columnType, execDecl, manyDecl, oneDecl, preamble, + execlastidDecl, }; diff --git a/src/drivers/postgres.ts b/src/drivers/postgres.ts index 1a2b575..f9d7bc8 100644 --- a/src/drivers/postgres.ts +++ b/src/drivers/postgres.ts @@ -1,4 +1,4 @@ -import { SyntaxKind, NodeFlags, TypeNode, factory } from "typescript"; +import { SyntaxKind, NodeFlags, TypeNode, factory, FunctionDeclaration } from "typescript"; import { Parameter, Column } from "../gen/plugin/codegen_pb"; import { argName, colName } from "./utlis"; @@ -602,10 +602,20 @@ export function oneDecl( ); } +export function execlastidDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + params: Parameter[] +): FunctionDeclaration { + throw new Error('postgres driver currently does not support :execlastid') +} + export default { columnType, preamble, execDecl, manyDecl, oneDecl, + execlastidDecl }; From ab29438ed3a26d8105e2f42c3b42169c94a461c2 Mon Sep 17 00:00:00 2001 From: Kyle Gray Date: Sat, 2 Mar 2024 20:36:24 -0800 Subject: [PATCH 07/12] chore: Enable CI for pull requests (#20) * chore: Enable CI for pull requests * Prevent duplicate builds --- .github/workflows/ci.yml | 6 +++++- .github/workflows/examples.yml | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2781563..f576664 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,9 @@ name: ci -on: [push] +on: + push: + branches: + - main + pull_request: jobs: build: runs-on: ubuntu-latest diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 6defb1a..57776cd 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -1,5 +1,9 @@ name: examples -on: [push] +on: + push: + branches: + - main + pull_request: jobs: bun-mysql: runs-on: ubuntu-latest From 029aec71c3ca7454685a937a2a8e1609a330c423 Mon Sep 17 00:00:00 2001 From: Kyle Gray Date: Sat, 2 Mar 2024 20:45:26 -0800 Subject: [PATCH 08/12] refactor: Make drivers classes instead of modules (#21) * WIP * postgres * Change driver from module to class --- src/app.ts | 18 +- src/drivers/better-sqlite3.ts | 730 ++++++++--------- src/drivers/mysql2.ts | 1299 +++++++++++++++-------------- src/drivers/pg.ts | 1450 ++++++++++++++++----------------- src/drivers/postgres.ts | 1059 ++++++++++++------------ 5 files changed, 2272 insertions(+), 2284 deletions(-) diff --git a/src/app.ts b/src/app.ts index 36e1ddd..e1dec6f 100644 --- a/src/app.ts +++ b/src/app.ts @@ -28,10 +28,10 @@ import { } from "./gen/plugin/codegen_pb"; import { argName, colName } from "./drivers/utlis"; -import betterSQLite3 from "./drivers/better-sqlite3"; -import pg from "./drivers/pg"; -import postgres from "./drivers/postgres"; -import mysql2 from "./drivers/mysql2"; +import { Driver as Sqlite3Driver } from "./drivers/better-sqlite3"; +import { Driver as PgDriver } from "./drivers/pg"; +import { Driver as PostgresDriver } from "./drivers/postgres"; +import { Driver as MysqlDriver } from "./drivers/mysql2"; // Read input from stdin const input = readInput(); @@ -81,16 +81,16 @@ interface Driver { function createNodeGenerator(driver?: string): Driver { switch (driver) { case "mysql2": { - return mysql2; + return new MysqlDriver(); } case "pg": { - return pg; + return new PgDriver(); } case "postgres": { - return postgres; + return new PostgresDriver(); } case "better-sqlite3": { - return betterSQLite3; + return new Sqlite3Driver(); } } throw new Error(`unknown driver: ${driver}`); @@ -169,7 +169,7 @@ ${query.text}` case ":execlastid": { nodes.push( driver.execlastidDecl(lowerName, textName, argIface, query.params) - ) + ); break; } case ":one": { diff --git a/src/drivers/better-sqlite3.ts b/src/drivers/better-sqlite3.ts index 8665b91..c6210e8 100644 --- a/src/drivers/better-sqlite3.ts +++ b/src/drivers/better-sqlite3.ts @@ -1,96 +1,15 @@ -import { SyntaxKind, NodeFlags, Node, TypeNode, factory, FunctionDeclaration } from "typescript"; +import { + SyntaxKind, + NodeFlags, + Node, + TypeNode, + factory, + FunctionDeclaration, +} from "typescript"; import { Parameter, Column, Query } from "../gen/plugin/codegen_pb"; import { argName } from "./utlis"; -/** - * {@link https://github.com/WiseLibs/better-sqlite3/blob/v9.4.1/docs/api.md#binding-parameters} - * {@link https://github.com/sqlc-dev/sqlc/blob/v1.25.0/internal/codegen/golang/sqlite_type.go} - */ -export function columnType(column?: Column): TypeNode { - if (column === undefined || column.type === undefined) { - return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); - } - - let typ: TypeNode = factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); - switch (column.type.name) { - case "int": - case "integer": - case "tinyint": - case "smallint": - case "mediumint": - case "bigint": - case "unsignedbigint": - case "int2": - case "int8": { - // TODO: Improve `BigInt` handling (https://github.com/WiseLibs/better-sqlite3/blob/v9.4.1/docs/integer.md) - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "blob": { - // TODO: Is this correct or node-specific? - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Buffer"), - undefined - ); - break; - } - case "real": - case "double": - case "doubleprecision": - case "float": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "boolean": - case "bool": { - typ = factory.createKeywordTypeNode(SyntaxKind.BooleanKeyword); - break; - } - case "date": - case "datetime": - case "timestamp": { - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Date"), - undefined - ); - break; - } - } - - if (column.notNull) { - return typ; - } - - return factory.createUnionTypeNode([ - typ, - factory.createLiteralTypeNode(factory.createNull()), - ]); -} - -export function preamble(queries: Query[]) { - const imports: Node[] = [ - factory.createImportDeclaration( - undefined, - factory.createImportClause( - false, - undefined, - factory.createNamedImports([ - factory.createImportSpecifier( - false, - undefined, - factory.createIdentifier("Database") - ), - ]) - ), - factory.createStringLiteral("better-sqlite3"), - undefined - ), - ]; - - return imports; -} - function funcParamsDecl(iface: string | undefined, params: Parameter[]) { let funcParams = [ factory.createParameterDeclaration( @@ -125,322 +44,395 @@ function funcParamsDecl(iface: string | undefined, params: Parameter[]) { return funcParams; } -export function execDecl( - funcName: string, - queryName: string, - argIface: string | undefined, - params: Parameter[] -) { - const funcParams = funcParamsDecl(argIface, params); +export class Driver { + /** + * {@link https://github.com/WiseLibs/better-sqlite3/blob/v9.4.1/docs/api.md#binding-parameters} + * {@link https://github.com/sqlc-dev/sqlc/blob/v1.25.0/internal/codegen/golang/sqlite_type.go} + */ + columnType(column?: Column): TypeNode { + if (column === undefined || column.type === undefined) { + return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); + } - return factory.createFunctionDeclaration( - [ - factory.createToken(SyntaxKind.ExportKeyword), - factory.createToken(SyntaxKind.AsyncKeyword), - ], - undefined, - factory.createIdentifier(funcName), - undefined, - funcParams, - factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ - factory.createKeywordTypeNode(SyntaxKind.VoidKeyword), - ]), - factory.createBlock( - [ - factory.createVariableStatement( + let typ: TypeNode = factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); + switch (column.type.name) { + case "int": + case "integer": + case "tinyint": + case "smallint": + case "mediumint": + case "bigint": + case "unsignedbigint": + case "int2": + case "int8": { + // TODO: Improve `BigInt` handling (https://github.com/WiseLibs/better-sqlite3/blob/v9.4.1/docs/integer.md) + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "blob": { + // TODO: Is this correct or node-specific? + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Buffer"), + undefined + ); + break; + } + case "real": + case "double": + case "doubleprecision": + case "float": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "boolean": + case "bool": { + typ = factory.createKeywordTypeNode(SyntaxKind.BooleanKeyword); + break; + } + case "date": + case "datetime": + case "timestamp": { + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Date"), + undefined + ); + break; + } + } + + if (column.notNull) { + return typ; + } + + return factory.createUnionTypeNode([ + typ, + factory.createLiteralTypeNode(factory.createNull()), + ]); + } + + preamble(queries: Query[]) { + const imports: Node[] = [ + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - factory.createIdentifier("stmt"), - undefined, - undefined, - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier("database"), - factory.createIdentifier("prepare") - ), - undefined, - [ - factory.createIdentifier(queryName) - ] - ) - ), - ], - NodeFlags.Const | - // ts.NodeFlags.Constant | - // NodeFlags.AwaitContext | - // ts.NodeFlags.Constant | - // NodeFlags.ContextFlags | - NodeFlags.TypeExcludesFlags - ) - ), - factory.createExpressionStatement( - factory.createAwaitExpression( - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier("stmt"), - factory.createIdentifier("run") - ), + factory.createNamedImports([ + factory.createImportSpecifier( + false, undefined, - params.map((param, i) => - factory.createPropertyAccessExpression( - factory.createIdentifier("args"), - factory.createIdentifier(argName(i, param.column)) - ) - ), - ) - ) + factory.createIdentifier("Database") + ), + ]) ), - ], - true - ) - ); -} + factory.createStringLiteral("better-sqlite3"), + undefined + ), + ]; -export function oneDecl( - funcName: string, - queryName: string, - argIface: string | undefined, - returnIface: string, - params: Parameter[], - columns: Column[] -) { - const funcParams = funcParamsDecl(argIface, params); + return imports; + } - return factory.createFunctionDeclaration( - [ - factory.createToken(SyntaxKind.ExportKeyword), - factory.createToken(SyntaxKind.AsyncKeyword), - ], - undefined, - factory.createIdentifier(funcName), - undefined, - funcParams, - factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ - factory.createUnionTypeNode([ - factory.createTypeReferenceNode( - factory.createIdentifier(returnIface), - undefined - ), - factory.createLiteralTypeNode(factory.createNull()), - ]), - ]), - factory.createBlock( + execDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + params: Parameter[] + ) { + const funcParams = funcParamsDecl(argIface, params); + + return factory.createFunctionDeclaration( [ - factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - factory.createIdentifier("stmt"), - undefined, + factory.createToken(SyntaxKind.ExportKeyword), + factory.createToken(SyntaxKind.AsyncKeyword), + ], + undefined, + factory.createIdentifier(funcName), + undefined, + funcParams, + factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ + factory.createKeywordTypeNode(SyntaxKind.VoidKeyword), + ]), + factory.createBlock( + [ + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createIdentifier("stmt"), + undefined, + undefined, + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("database"), + factory.createIdentifier("prepare") + ), + undefined, + [factory.createIdentifier(queryName)] + ) + ), + ], + NodeFlags.Const | + // ts.NodeFlags.Constant | + // NodeFlags.AwaitContext | + // ts.NodeFlags.Constant | + // NodeFlags.ContextFlags | + NodeFlags.TypeExcludesFlags + ) + ), + factory.createExpressionStatement( + factory.createAwaitExpression( + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("stmt"), + factory.createIdentifier("run") + ), undefined, - factory.createCallExpression( + params.map((param, i) => factory.createPropertyAccessExpression( - factory.createIdentifier("database"), - factory.createIdentifier("prepare") - ), - undefined, - [ - factory.createIdentifier(queryName) - ] + factory.createIdentifier("args"), + factory.createIdentifier(argName(i, param.column)) + ) ) - ), - ], - NodeFlags.Const | - // ts.NodeFlags.Constant | - // NodeFlags.AwaitContext | - // ts.NodeFlags.Constant | - // NodeFlags.ContextFlags | - NodeFlags.TypeExcludesFlags - ) - ), - factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - factory.createIdentifier("result"), - undefined, - undefined, - factory.createAwaitExpression( + ) + ) + ), + ], + true + ) + ); + } + + oneDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + returnIface: string, + params: Parameter[], + columns: Column[] + ) { + const funcParams = funcParamsDecl(argIface, params); + + return factory.createFunctionDeclaration( + [ + factory.createToken(SyntaxKind.ExportKeyword), + factory.createToken(SyntaxKind.AsyncKeyword), + ], + undefined, + factory.createIdentifier(funcName), + undefined, + funcParams, + factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ + factory.createUnionTypeNode([ + factory.createTypeReferenceNode( + factory.createIdentifier(returnIface), + undefined + ), + factory.createLiteralTypeNode(factory.createNull()), + ]), + ]), + factory.createBlock( + [ + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createIdentifier("stmt"), + undefined, + undefined, factory.createCallExpression( factory.createPropertyAccessExpression( - factory.createIdentifier("stmt"), - factory.createIdentifier("get") + factory.createIdentifier("database"), + factory.createIdentifier("prepare") ), undefined, - params.map((param, i) => + [factory.createIdentifier(queryName)] + ) + ), + ], + NodeFlags.Const | + // ts.NodeFlags.Constant | + // NodeFlags.AwaitContext | + // ts.NodeFlags.Constant | + // NodeFlags.ContextFlags | + NodeFlags.TypeExcludesFlags + ) + ), + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createIdentifier("result"), + undefined, + undefined, + factory.createAwaitExpression( + factory.createCallExpression( factory.createPropertyAccessExpression( - factory.createIdentifier("args"), - factory.createIdentifier( - argName(i, param.column) + factory.createIdentifier("stmt"), + factory.createIdentifier("get") + ), + undefined, + params.map((param, i) => + factory.createPropertyAccessExpression( + factory.createIdentifier("args"), + factory.createIdentifier(argName(i, param.column)) ) ) - ), + ) ) - ) - ), - ], - NodeFlags.Const | - // ts.NodeFlags.Constant | - NodeFlags.AwaitContext | - // ts.NodeFlags.Constant | - NodeFlags.ContextFlags | - NodeFlags.TypeExcludesFlags - ) - ), - factory.createIfStatement( - factory.createBinaryExpression( - factory.createIdentifier("result"), - factory.createToken(SyntaxKind.EqualsEqualsToken), - factory.createIdentifier("undefined") + ), + ], + NodeFlags.Const | + // ts.NodeFlags.Constant | + NodeFlags.AwaitContext | + // ts.NodeFlags.Constant | + NodeFlags.ContextFlags | + NodeFlags.TypeExcludesFlags + ) ), - factory.createBlock( - [factory.createReturnStatement(factory.createNull())], - true + factory.createIfStatement( + factory.createBinaryExpression( + factory.createIdentifier("result"), + factory.createToken(SyntaxKind.EqualsEqualsToken), + factory.createIdentifier("undefined") + ), + factory.createBlock( + [factory.createReturnStatement(factory.createNull())], + true + ), + undefined ), - undefined - ), - factory.createReturnStatement( - factory.createAsExpression( - factory.createIdentifier("result"), - factory.createTypeReferenceNode( - factory.createIdentifier(returnIface), - undefined + factory.createReturnStatement( + factory.createAsExpression( + factory.createIdentifier("result"), + factory.createTypeReferenceNode( + factory.createIdentifier(returnIface), + undefined + ) ) - ) - ), - ], - true - ) - ); -} + ), + ], + true + ) + ); + } -export function manyDecl( - funcName: string, - queryName: string, - argIface: string | undefined, - returnIface: string, - params: Parameter[], - columns: Column[] -) { - const funcParams = funcParamsDecl(argIface, params); + manyDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + returnIface: string, + params: Parameter[], + columns: Column[] + ) { + const funcParams = funcParamsDecl(argIface, params); - return factory.createFunctionDeclaration( - [ - factory.createToken(SyntaxKind.ExportKeyword), - factory.createToken(SyntaxKind.AsyncKeyword), - ], - undefined, - factory.createIdentifier(funcName), - undefined, - funcParams, - factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ - factory.createArrayTypeNode( - factory.createTypeReferenceNode( - factory.createIdentifier(returnIface), - undefined - ) - ), - ]), - factory.createBlock( + return factory.createFunctionDeclaration( [ - factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - factory.createIdentifier("stmt"), - undefined, - undefined, - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier("database"), - factory.createIdentifier("prepare") - ), - undefined, - [ - factory.createIdentifier(queryName) - ] - ) - ), - ], - NodeFlags.Const | - // ts.NodeFlags.Constant | - // NodeFlags.AwaitContext | - // ts.NodeFlags.Constant | - // NodeFlags.ContextFlags | - NodeFlags.TypeExcludesFlags + factory.createToken(SyntaxKind.ExportKeyword), + factory.createToken(SyntaxKind.AsyncKeyword), + ], + undefined, + factory.createIdentifier(funcName), + undefined, + funcParams, + factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ + factory.createArrayTypeNode( + factory.createTypeReferenceNode( + factory.createIdentifier(returnIface), + undefined ) ), - factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - factory.createIdentifier("result"), - undefined, - undefined, - factory.createAwaitExpression( + ]), + factory.createBlock( + [ + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createIdentifier("stmt"), + undefined, + undefined, factory.createCallExpression( factory.createPropertyAccessExpression( - factory.createIdentifier("stmt"), - factory.createIdentifier("all") + factory.createIdentifier("database"), + factory.createIdentifier("prepare") ), undefined, - params.map((param, i) => + [factory.createIdentifier(queryName)] + ) + ), + ], + NodeFlags.Const | + // ts.NodeFlags.Constant | + // NodeFlags.AwaitContext | + // ts.NodeFlags.Constant | + // NodeFlags.ContextFlags | + NodeFlags.TypeExcludesFlags + ) + ), + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createIdentifier("result"), + undefined, + undefined, + factory.createAwaitExpression( + factory.createCallExpression( factory.createPropertyAccessExpression( - factory.createIdentifier("args"), - factory.createIdentifier( - argName(i, param.column) + factory.createIdentifier("stmt"), + factory.createIdentifier("all") + ), + undefined, + params.map((param, i) => + factory.createPropertyAccessExpression( + factory.createIdentifier("args"), + factory.createIdentifier(argName(i, param.column)) ) ) - ), + ) ) + ), + ], + NodeFlags.Const | + // NodeFlags.Constant | + NodeFlags.AwaitContext | + // NodeFlags.Constant | + NodeFlags.ContextFlags | + NodeFlags.TypeExcludesFlags + ) + ), + factory.createReturnStatement( + factory.createAsExpression( + factory.createIdentifier("result"), + factory.createArrayTypeNode( + factory.createTypeReferenceNode( + factory.createIdentifier(returnIface), + undefined ) - ), - ], - NodeFlags.Const | - // NodeFlags.Constant | - NodeFlags.AwaitContext | - // NodeFlags.Constant | - NodeFlags.ContextFlags | - NodeFlags.TypeExcludesFlags - ) - ), - factory.createReturnStatement( - factory.createAsExpression( - factory.createIdentifier("result"), - factory.createArrayTypeNode( - factory.createTypeReferenceNode( - factory.createIdentifier(returnIface), - undefined ) ) - ) - ), - ], - true - ) - ); -} + ), + ], + true + ) + ); + } -export function execlastidDecl( - funcName: string, - queryName: string, - argIface: string | undefined, - params: Parameter[] -): FunctionDeclaration { - throw new Error('better-sqlite3 driver currently does not support :execlastid') + execlastidDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + params: Parameter[] + ): FunctionDeclaration { + throw new Error( + "better-sqlite3 driver currently does not support :execlastid" + ); + } } - -export default { - columnType, - execDecl, - manyDecl, - oneDecl, - preamble, - execlastidDecl, -}; diff --git a/src/drivers/mysql2.ts b/src/drivers/mysql2.ts index bc052bb..731db8d 100644 --- a/src/drivers/mysql2.ts +++ b/src/drivers/mysql2.ts @@ -5,218 +5,6 @@ import { SyntaxKind, NodeFlags, TypeNode, factory } from "typescript"; import { Parameter, Column, Query } from "../gen/plugin/codegen_pb"; import { argName, colName } from "./utlis"; -export function columnType(column?: Column): TypeNode { - if (column === undefined || column.type === undefined) { - return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); - } - let typ: TypeNode = factory.createKeywordTypeNode(SyntaxKind.StringKeyword); - - switch (column.type.name) { - case "bigint": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "binary": { - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Buffer"), - undefined - ); - break; - } - case "bit": { - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Buffer"), - undefined - ); - break; - } - case "blob": { - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Buffer"), - undefined - ); - break; - } - case "char": { - // string - break; - } - case "date": { - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Date"), - undefined - ); - break; - } - case "datetime": { - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Date"), - undefined - ); - break; - } - case "decimal": { - // string - break; - } - case "double": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "float": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "int": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "longblob": { - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Buffer"), - undefined - ); - break; - } - case "longtext": { - // string - break; - } - case "mediumblob": { - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Buffer"), - undefined - ); - break; - } - case "mediumint": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "mediumtext": { - // string - break; - } - case "smallint": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "text": { - // string - break; - } - case "time": { - // string - break; - } - case "timestamp": { - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Date"), - undefined - ); - break; - } - case "tinyblob": { - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Buffer"), - undefined - ); - break; - } - case "tinyint": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "tinytext": { - // string - break; - } - case "json": { - typ = factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); - break; - } - case "varbinary": { - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Buffer"), - undefined - ); - break; - } - case "varchar": { - // string - break; - } - case "year": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - // default: { - // const output = new TextEncoder().encode(column.type.name + "\n"); - // const buffer = new Uint8Array(output); - // writeFileSync(STDIO.Stderr, buffer); - // } - } - if (column.notNull) { - return typ; - } - return factory.createUnionTypeNode([ - typ, - factory.createLiteralTypeNode(factory.createNull()), - ]); -} - -export function preamble(queries: Query[]) { - const hasExecLastIdCmd = queries.some((query) => query.cmd === ":execlastid"); - return [ - factory.createImportDeclaration( - undefined, - factory.createImportClause( - false, - factory.createIdentifier("mysql"), - factory.createNamedImports([ - factory.createImportSpecifier( - false, - undefined, - factory.createIdentifier("RowDataPacket") - ), - ...(hasExecLastIdCmd - ? [ - factory.createImportSpecifier( - false, - undefined, - factory.createIdentifier("ResultSetHeader") - ), - ] - : []), - ]) - ), - factory.createStringLiteral("mysql2/promise"), - undefined - ), - factory.createTypeAliasDeclaration( - undefined, - factory.createIdentifier("Client"), - undefined, - factory.createUnionTypeNode([ - factory.createTypeReferenceNode( - factory.createQualifiedName( - factory.createIdentifier("mysql"), - factory.createIdentifier("Connection") - ), - undefined - ), - factory.createTypeReferenceNode( - factory.createQualifiedName( - factory.createIdentifier("mysql"), - factory.createIdentifier("Pool") - ), - undefined - ), - ]) - ), - ]; -} - function funcParamsDecl(iface: string | undefined, params: Parameter[]) { let funcParams = [ factory.createParameterDeclaration( @@ -251,484 +39,691 @@ function funcParamsDecl(iface: string | undefined, params: Parameter[]) { return funcParams; } -export function execDecl( - funcName: string, - queryName: string, - argIface: string | undefined, - params: Parameter[] -) { - const funcParams = funcParamsDecl(argIface, params); +export class Driver { + columnType(column?: Column): TypeNode { + if (column === undefined || column.type === undefined) { + return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); + } + let typ: TypeNode = factory.createKeywordTypeNode(SyntaxKind.StringKeyword); - return factory.createFunctionDeclaration( - [ - factory.createToken(SyntaxKind.ExportKeyword), - factory.createToken(SyntaxKind.AsyncKeyword), - ], - undefined, - factory.createIdentifier(funcName), - undefined, - funcParams, - factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ - factory.createKeywordTypeNode(SyntaxKind.VoidKeyword), - ]), - factory.createBlock( - [ - factory.createExpressionStatement( - factory.createAwaitExpression( - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier("client"), - factory.createIdentifier("query") - ), + switch (column.type.name) { + case "bigint": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "binary": { + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Buffer"), + undefined + ); + break; + } + case "bit": { + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Buffer"), + undefined + ); + break; + } + case "blob": { + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Buffer"), + undefined + ); + break; + } + case "char": { + // string + break; + } + case "date": { + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Date"), + undefined + ); + break; + } + case "datetime": { + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Date"), + undefined + ); + break; + } + case "decimal": { + // string + break; + } + case "double": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "float": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "int": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "longblob": { + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Buffer"), + undefined + ); + break; + } + case "longtext": { + // string + break; + } + case "mediumblob": { + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Buffer"), + undefined + ); + break; + } + case "mediumint": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "mediumtext": { + // string + break; + } + case "smallint": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "text": { + // string + break; + } + case "time": { + // string + break; + } + case "timestamp": { + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Date"), + undefined + ); + break; + } + case "tinyblob": { + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Buffer"), + undefined + ); + break; + } + case "tinyint": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "tinytext": { + // string + break; + } + case "json": { + typ = factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); + break; + } + case "varbinary": { + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Buffer"), + undefined + ); + break; + } + case "varchar": { + // string + break; + } + case "year": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + // default: { + // const output = new TextEncoder().encode(column.type.name + "\n"); + // const buffer = new Uint8Array(output); + // writeFileSync(STDIO.Stderr, buffer); + // } + } + if (column.notNull) { + return typ; + } + return factory.createUnionTypeNode([ + typ, + factory.createLiteralTypeNode(factory.createNull()), + ]); + } + + preamble(queries: Query[]) { + const hasExecLastIdCmd = queries.some( + (query) => query.cmd === ":execlastid" + ); + return [ + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + factory.createIdentifier("mysql"), + factory.createNamedImports([ + factory.createImportSpecifier( + false, undefined, - [ - factory.createObjectLiteralExpression( - [ - factory.createPropertyAssignment( - factory.createIdentifier("sql"), - factory.createIdentifier(queryName) - ), - factory.createPropertyAssignment( - factory.createIdentifier("values"), - factory.createArrayLiteralExpression( - params.map((param, i) => - factory.createPropertyAccessExpression( - factory.createIdentifier("args"), - factory.createIdentifier(argName(i, param.column)) - ) - ), - false - ) - ), - ], - true - ), - ] - ) - ) + factory.createIdentifier("RowDataPacket") + ), + ...(hasExecLastIdCmd + ? [ + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("ResultSetHeader") + ), + ] + : []), + ]) ), - ], - true - ) - ); -} + factory.createStringLiteral("mysql2/promise"), + undefined + ), + factory.createTypeAliasDeclaration( + undefined, + factory.createIdentifier("Client"), + undefined, + factory.createUnionTypeNode([ + factory.createTypeReferenceNode( + factory.createQualifiedName( + factory.createIdentifier("mysql"), + factory.createIdentifier("Connection") + ), + undefined + ), + factory.createTypeReferenceNode( + factory.createQualifiedName( + factory.createIdentifier("mysql"), + factory.createIdentifier("Pool") + ), + undefined + ), + ]) + ), + ]; + } -export function manyDecl( - funcName: string, - queryName: string, - argIface: string | undefined, - returnIface: string, - params: Parameter[], - columns: Column[] -) { - const funcParams = funcParamsDecl(argIface, params); + execDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + params: Parameter[] + ) { + const funcParams = funcParamsDecl(argIface, params); - return factory.createFunctionDeclaration( - [ - factory.createToken(SyntaxKind.ExportKeyword), - factory.createToken(SyntaxKind.AsyncKeyword), - ], - undefined, - factory.createIdentifier(funcName), - undefined, - funcParams, - factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ - factory.createArrayTypeNode( - factory.createTypeReferenceNode( - factory.createIdentifier(returnIface), - undefined - ) - ), - ]), - factory.createBlock( + return factory.createFunctionDeclaration( [ - factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - factory.createArrayBindingPattern([ - factory.createBindingElement( - undefined, - undefined, - factory.createIdentifier("rows"), - undefined - ), - ]), - undefined, + factory.createToken(SyntaxKind.ExportKeyword), + factory.createToken(SyntaxKind.AsyncKeyword), + ], + undefined, + factory.createIdentifier(funcName), + undefined, + funcParams, + factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ + factory.createKeywordTypeNode(SyntaxKind.VoidKeyword), + ]), + factory.createBlock( + [ + factory.createExpressionStatement( + factory.createAwaitExpression( + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("client"), + factory.createIdentifier("query") + ), undefined, - factory.createAwaitExpression( - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier("client"), - factory.createIdentifier("query") - ), + [ + factory.createObjectLiteralExpression( [ - factory.createArrayTypeNode( - factory.createTypeReferenceNode( - factory.createIdentifier("RowDataPacket"), - undefined - ) + factory.createPropertyAssignment( + factory.createIdentifier("sql"), + factory.createIdentifier(queryName) ), - ], - [ - factory.createObjectLiteralExpression( - [ - factory.createPropertyAssignment( - factory.createIdentifier("sql"), - factory.createIdentifier(queryName) - ), - factory.createPropertyAssignment( - factory.createIdentifier("values"), - factory.createArrayLiteralExpression( - params.map((param, i) => - factory.createPropertyAccessExpression( - factory.createIdentifier("args"), - factory.createIdentifier( - argName(i, param.column) - ) - ) - ), - false + factory.createPropertyAssignment( + factory.createIdentifier("values"), + factory.createArrayLiteralExpression( + params.map((param, i) => + factory.createPropertyAccessExpression( + factory.createIdentifier("args"), + factory.createIdentifier(argName(i, param.column)) ) ), - factory.createPropertyAssignment( - factory.createIdentifier("rowsAsArray"), - factory.createTrue() - ), - ], - true + false + ) ), - ] - ) - ) - ), - ], - NodeFlags.Const | - // NodeFlags.Constant | - NodeFlags.AwaitContext | - // NodeFlags.Constant | - NodeFlags.ContextFlags | - NodeFlags.TypeExcludesFlags + ], + true + ), + ] + ) + ) + ), + ], + true + ) + ); + } + + manyDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + returnIface: string, + params: Parameter[], + columns: Column[] + ) { + const funcParams = funcParamsDecl(argIface, params); + + return factory.createFunctionDeclaration( + [ + factory.createToken(SyntaxKind.ExportKeyword), + factory.createToken(SyntaxKind.AsyncKeyword), + ], + undefined, + factory.createIdentifier(funcName), + undefined, + funcParams, + factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ + factory.createArrayTypeNode( + factory.createTypeReferenceNode( + factory.createIdentifier(returnIface), + undefined ) ), - factory.createReturnStatement( - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier("rows"), - factory.createIdentifier("map") - ), + ]), + factory.createBlock( + [ + factory.createVariableStatement( undefined, - [ - factory.createArrowFunction( - undefined, - undefined, - [ - factory.createParameterDeclaration( - undefined, - undefined, - factory.createIdentifier("row"), - undefined, - undefined, - undefined - ), - ], - undefined, - factory.createToken(SyntaxKind.EqualsGreaterThanToken), - factory.createBlock( - [ - factory.createReturnStatement( - factory.createObjectLiteralExpression( - columns.map((col, i) => - factory.createPropertyAssignment( - factory.createIdentifier(colName(i, col)), - factory.createElementAccessExpression( - factory.createIdentifier("row"), - factory.createNumericLiteral(`${i}`) - ) + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createArrayBindingPattern([ + factory.createBindingElement( + undefined, + undefined, + factory.createIdentifier("rows"), + undefined + ), + ]), + undefined, + undefined, + factory.createAwaitExpression( + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("client"), + factory.createIdentifier("query") + ), + [ + factory.createArrayTypeNode( + factory.createTypeReferenceNode( + factory.createIdentifier("RowDataPacket"), + undefined ) ), - true - ) + ], + [ + factory.createObjectLiteralExpression( + [ + factory.createPropertyAssignment( + factory.createIdentifier("sql"), + factory.createIdentifier(queryName) + ), + factory.createPropertyAssignment( + factory.createIdentifier("values"), + factory.createArrayLiteralExpression( + params.map((param, i) => + factory.createPropertyAccessExpression( + factory.createIdentifier("args"), + factory.createIdentifier( + argName(i, param.column) + ) + ) + ), + false + ) + ), + factory.createPropertyAssignment( + factory.createIdentifier("rowsAsArray"), + factory.createTrue() + ), + ], + true + ), + ] + ) + ) + ), + ], + NodeFlags.Const | + // NodeFlags.Constant | + NodeFlags.AwaitContext | + // NodeFlags.Constant | + NodeFlags.ContextFlags | + NodeFlags.TypeExcludesFlags + ) + ), + factory.createReturnStatement( + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("rows"), + factory.createIdentifier("map") + ), + undefined, + [ + factory.createArrowFunction( + undefined, + undefined, + [ + factory.createParameterDeclaration( + undefined, + undefined, + factory.createIdentifier("row"), + undefined, + undefined, + undefined ), ], - true - ) - ), - ] - ) - ), - ], - true - ) - ); -} + undefined, + factory.createToken(SyntaxKind.EqualsGreaterThanToken), + factory.createBlock( + [ + factory.createReturnStatement( + factory.createObjectLiteralExpression( + columns.map((col, i) => + factory.createPropertyAssignment( + factory.createIdentifier(colName(i, col)), + factory.createElementAccessExpression( + factory.createIdentifier("row"), + factory.createNumericLiteral(`${i}`) + ) + ) + ), + true + ) + ), + ], + true + ) + ), + ] + ) + ), + ], + true + ) + ); + } -export function oneDecl( - funcName: string, - queryName: string, - argIface: string | undefined, - returnIface: string, - params: Parameter[], - columns: Column[] -) { - const funcParams = funcParamsDecl(argIface, params); + oneDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + returnIface: string, + params: Parameter[], + columns: Column[] + ) { + const funcParams = funcParamsDecl(argIface, params); - return factory.createFunctionDeclaration( - [ - factory.createToken(SyntaxKind.ExportKeyword), - factory.createToken(SyntaxKind.AsyncKeyword), - ], - undefined, - factory.createIdentifier(funcName), - undefined, - funcParams, - factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ - factory.createUnionTypeNode([ - factory.createTypeReferenceNode( - factory.createIdentifier(returnIface), - undefined - ), - factory.createLiteralTypeNode(factory.createNull()), - ]), - ]), - factory.createBlock( + return factory.createFunctionDeclaration( [ - factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - factory.createArrayBindingPattern([ - factory.createBindingElement( - undefined, - undefined, - factory.createIdentifier("rows"), - undefined - ), - ]), - undefined, - undefined, - factory.createAwaitExpression( - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier("client"), - factory.createIdentifier("query") + factory.createToken(SyntaxKind.ExportKeyword), + factory.createToken(SyntaxKind.AsyncKeyword), + ], + undefined, + factory.createIdentifier(funcName), + undefined, + funcParams, + factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ + factory.createUnionTypeNode([ + factory.createTypeReferenceNode( + factory.createIdentifier(returnIface), + undefined + ), + factory.createLiteralTypeNode(factory.createNull()), + ]), + ]), + factory.createBlock( + [ + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createArrayBindingPattern([ + factory.createBindingElement( + undefined, + undefined, + factory.createIdentifier("rows"), + undefined ), - [ - factory.createArrayTypeNode( - factory.createTypeReferenceNode( - factory.createIdentifier("RowDataPacket"), - undefined - ) + ]), + undefined, + undefined, + factory.createAwaitExpression( + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("client"), + factory.createIdentifier("query") ), - ], - [ - factory.createObjectLiteralExpression( - [ - factory.createPropertyAssignment( - factory.createIdentifier("sql"), - factory.createIdentifier(queryName) - ), - factory.createPropertyAssignment( - factory.createIdentifier("values"), - factory.createArrayLiteralExpression( - params.map((param, i) => - factory.createPropertyAccessExpression( - factory.createIdentifier("args"), - factory.createIdentifier( - argName(i, param.column) + [ + factory.createArrayTypeNode( + factory.createTypeReferenceNode( + factory.createIdentifier("RowDataPacket"), + undefined + ) + ), + ], + [ + factory.createObjectLiteralExpression( + [ + factory.createPropertyAssignment( + factory.createIdentifier("sql"), + factory.createIdentifier(queryName) + ), + factory.createPropertyAssignment( + factory.createIdentifier("values"), + factory.createArrayLiteralExpression( + params.map((param, i) => + factory.createPropertyAccessExpression( + factory.createIdentifier("args"), + factory.createIdentifier( + argName(i, param.column) + ) ) - ) - ), - false - ) - ), - factory.createPropertyAssignment( - factory.createIdentifier("rowsAsArray"), - factory.createTrue() - ), - ], - true - ), - ] + ), + false + ) + ), + factory.createPropertyAssignment( + factory.createIdentifier("rowsAsArray"), + factory.createTrue() + ), + ], + true + ), + ] + ) ) - ) + ), + ], + NodeFlags.Const | + // ts.NodeFlags.Constant | + NodeFlags.AwaitContext | + // ts.NodeFlags.Constant | + NodeFlags.ContextFlags | + NodeFlags.TypeExcludesFlags + ) + ), + factory.createIfStatement( + factory.createBinaryExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("rows"), + factory.createIdentifier("length") ), - ], - NodeFlags.Const | - // ts.NodeFlags.Constant | - NodeFlags.AwaitContext | - // ts.NodeFlags.Constant | - NodeFlags.ContextFlags | - NodeFlags.TypeExcludesFlags - ) - ), - factory.createIfStatement( - factory.createBinaryExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier("rows"), - factory.createIdentifier("length") + factory.createToken(SyntaxKind.ExclamationEqualsEqualsToken), + factory.createNumericLiteral("1") + ), + factory.createBlock( + [factory.createReturnStatement(factory.createNull())], + true ), - factory.createToken(SyntaxKind.ExclamationEqualsEqualsToken), - factory.createNumericLiteral("1") + undefined ), - factory.createBlock( - [factory.createReturnStatement(factory.createNull())], - true + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createIdentifier("row"), + undefined, + undefined, + factory.createElementAccessExpression( + factory.createIdentifier("rows"), + factory.createNumericLiteral("0") + ) + ), + ], + NodeFlags.Const | + // NodeFlags.Constant | + NodeFlags.AwaitContext | + // NodeFlags.Constant | + NodeFlags.ContextFlags | + NodeFlags.TypeExcludesFlags + ) ), - undefined - ), - factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - factory.createIdentifier("row"), - undefined, - undefined, - factory.createElementAccessExpression( - factory.createIdentifier("rows"), - factory.createNumericLiteral("0") + factory.createReturnStatement( + factory.createObjectLiteralExpression( + columns.map((col, i) => + factory.createPropertyAssignment( + factory.createIdentifier(colName(i, col)), + factory.createElementAccessExpression( + factory.createIdentifier("row"), + factory.createNumericLiteral(`${i}`) + ) ) ), - ], - NodeFlags.Const | - // NodeFlags.Constant | - NodeFlags.AwaitContext | - // NodeFlags.Constant | - NodeFlags.ContextFlags | - NodeFlags.TypeExcludesFlags - ) - ), - factory.createReturnStatement( - factory.createObjectLiteralExpression( - columns.map((col, i) => - factory.createPropertyAssignment( - factory.createIdentifier(colName(i, col)), - factory.createElementAccessExpression( - factory.createIdentifier("row"), - factory.createNumericLiteral(`${i}`) - ) - ) - ), - true - ) - ), - ], - true - ) - ); -} + true + ) + ), + ], + true + ) + ); + } -export function execlastidDecl( - funcName: string, - queryName: string, - argIface: string | undefined, - params: Parameter[] -) { - const funcParams = funcParamsDecl(argIface, params); + execlastidDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + params: Parameter[] + ) { + const funcParams = funcParamsDecl(argIface, params); - return factory.createFunctionDeclaration( - [ - factory.createToken(SyntaxKind.ExportKeyword), - factory.createToken(SyntaxKind.AsyncKeyword), - ], - undefined, - factory.createIdentifier(funcName), - undefined, - funcParams, - factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ - factory.createTypeReferenceNode("number", undefined), - ]), - factory.createBlock( + return factory.createFunctionDeclaration( [ - factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - factory.createArrayBindingPattern([ - factory.createBindingElement( - undefined, - undefined, - factory.createIdentifier("result"), - undefined - ), - ]), - undefined, - undefined, - factory.createAwaitExpression( - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier("client"), - factory.createIdentifier("query") + factory.createToken(SyntaxKind.ExportKeyword), + factory.createToken(SyntaxKind.AsyncKeyword), + ], + undefined, + factory.createIdentifier(funcName), + undefined, + funcParams, + factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ + factory.createTypeReferenceNode("number", undefined), + ]), + factory.createBlock( + [ + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createArrayBindingPattern([ + factory.createBindingElement( + undefined, + undefined, + factory.createIdentifier("result"), + undefined ), - [ - factory.createTypeReferenceNode( - factory.createIdentifier("ResultSetHeader"), - undefined + ]), + undefined, + undefined, + factory.createAwaitExpression( + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("client"), + factory.createIdentifier("query") ), - ], - [ - factory.createObjectLiteralExpression( - [ - factory.createPropertyAssignment( - factory.createIdentifier("sql"), - factory.createIdentifier(queryName) - ), - factory.createPropertyAssignment( - factory.createIdentifier("values"), - factory.createArrayLiteralExpression( - params.map((param, i) => - factory.createPropertyAccessExpression( - factory.createIdentifier("args"), - factory.createIdentifier( - argName(i, param.column) + [ + factory.createTypeReferenceNode( + factory.createIdentifier("ResultSetHeader"), + undefined + ), + ], + [ + factory.createObjectLiteralExpression( + [ + factory.createPropertyAssignment( + factory.createIdentifier("sql"), + factory.createIdentifier(queryName) + ), + factory.createPropertyAssignment( + factory.createIdentifier("values"), + factory.createArrayLiteralExpression( + params.map((param, i) => + factory.createPropertyAccessExpression( + factory.createIdentifier("args"), + factory.createIdentifier( + argName(i, param.column) + ) ) - ) - ), - false - ) - ), - ], - true - ), - ] + ), + false + ) + ), + ], + true + ), + ] + ) ) - ) + ), + ], + NodeFlags.Const | + // NodeFlags.Constant | + NodeFlags.AwaitContext | + // NodeFlags.Constant | + NodeFlags.ContextFlags | + NodeFlags.TypeExcludesFlags + ) + ), + factory.createReturnStatement( + factory.createBinaryExpression( + factory.createPropertyAccessChain( + factory.createIdentifier("result"), + factory.createToken(SyntaxKind.QuestionDotToken), + factory.createIdentifier("insertId") ), - ], - NodeFlags.Const | - // NodeFlags.Constant | - NodeFlags.AwaitContext | - // NodeFlags.Constant | - NodeFlags.ContextFlags | - NodeFlags.TypeExcludesFlags - ) - ), - factory.createReturnStatement( - factory.createBinaryExpression( - factory.createPropertyAccessChain( - factory.createIdentifier("result"), - factory.createToken(SyntaxKind.QuestionDotToken), - factory.createIdentifier("insertId") - ), - factory.createToken(SyntaxKind.QuestionQuestionToken), - factory.createNumericLiteral(0) - ) - ), - ], - true - ) - ); + factory.createToken(SyntaxKind.QuestionQuestionToken), + factory.createNumericLiteral(0) + ) + ), + ], + true + ) + ); + } } - -export default { - columnType, - preamble, - execDecl, - manyDecl, - oneDecl, - execlastidDecl, -}; diff --git a/src/drivers/pg.ts b/src/drivers/pg.ts index 9272035..95828cf 100644 --- a/src/drivers/pg.ts +++ b/src/drivers/pg.ts @@ -1,394 +1,15 @@ -import { SyntaxKind, NodeFlags, Node, TypeNode, factory, FunctionDeclaration } from "typescript"; +import { + SyntaxKind, + NodeFlags, + Node, + TypeNode, + factory, + FunctionDeclaration, +} from "typescript"; import { Parameter, Column, Query } from "../gen/plugin/codegen_pb"; import { argName, colName } from "./utlis"; -export function columnType(column?: Column): TypeNode { - if (column === undefined || column.type === undefined) { - return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); - } - // Some of the type names have the `pgcatalog.` prefix. Remove this. - let typeName = column.type.name; - const pgCatalog = "pg_catalog."; - if (typeName.startsWith(pgCatalog)) { - typeName = typeName.slice(pgCatalog.length); - } - let typ: TypeNode = factory.createKeywordTypeNode(SyntaxKind.StringKeyword); - switch (typeName) { - case "aclitem": { - // string - break; - } - case "bigserial": { - // string - break; - } - case "bit": { - // string - break; - } - case "bool": { - typ = factory.createKeywordTypeNode(SyntaxKind.BooleanKeyword); - break; - } - case "box": { - // string - break; - } - case "bpchar": { - // string - break; - } - case "bytea": { - // TODO: Is this correct or node-specific? - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Buffer"), - undefined - ); - break; - } - case "cid": { - // string - break; - } - case "cidr": { - // string - break; - } - case "circle": { - typ = factory.createTypeLiteralNode([ - factory.createPropertySignature( - undefined, - factory.createIdentifier("x"), - undefined, - factory.createKeywordTypeNode(SyntaxKind.NumberKeyword) - ), - factory.createPropertySignature( - undefined, - factory.createIdentifier("y"), - undefined, - factory.createKeywordTypeNode(SyntaxKind.NumberKeyword) - ), - factory.createPropertySignature( - undefined, - factory.createIdentifier("radius"), - undefined, - factory.createKeywordTypeNode(SyntaxKind.NumberKeyword) - ), - ]); - break; - } - case "date": { - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Date"), - undefined - ); - break; - } - case "float4": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "float8": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "inet": { - // string - break; - } - case "int2": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "int4": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "int8": { - // string - break; - } - case "interval": { - typ = factory.createTypeReferenceNode( - factory.createIdentifier("IPostgresInterval"), - undefined - ); - break; - } - case "json": { - typ = factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); - break; - } - case "jsonb": { - typ = factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); - break; - } - case "line": { - // string - break; - } - case "lseg": { - // string - break; - } - case "madaddr": { - // string - break; - } - case "madaddr8": { - // string - break; - } - case "money": { - // string - break; - } - case "oid": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "path": { - // string - break; - } - case "pg_node_tree": { - // string - break; - } - case "pg_snapshot": { - // string - break; - } - case "point": { - typ = factory.createTypeLiteralNode([ - factory.createPropertySignature( - undefined, - factory.createIdentifier("x"), - undefined, - factory.createKeywordTypeNode(SyntaxKind.NumberKeyword) - ), - factory.createPropertySignature( - undefined, - factory.createIdentifier("y"), - undefined, - factory.createKeywordTypeNode(SyntaxKind.NumberKeyword) - ), - ]); - break; - } - case "polygon": { - // string - break; - } - case "regproc": { - // string - break; - } - case "regrole": { - // string - break; - } - case "serial": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "serial2": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "serial4": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "serial8": { - // string - break; - } - case "smallserial": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "tid": { - // string - break; - } - case "text": { - // string - break; - } - case "time": { - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Date"), - undefined - ); - break; - } - case "timetz": { - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Date"), - undefined - ); - break; - } - case "timestamp": { - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Date"), - undefined - ); - break; - } - case "timestamptz": { - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Date"), - undefined - ); - break; - } - case "tsquery": { - // string - break; - } - case "tsvector": { - // string - break; - } - case "txid_snapshot": { - // string - break; - } - case "uuid": { - // string - break; - } - case "varbit": { - // string - break; - } - case "varchar": { - // string - break; - } - case "xid": { - // string - break; - } - case "xml": { - // string - break; - } - } - if (column.isArray || column.arrayDims > 0) { - let dims = Math.max(column.arrayDims || 1); - for (let i = 0; i < dims; i++) { - typ = factory.createArrayTypeNode(typ); - } - } - if (column.notNull) { - return typ; - } - return factory.createUnionTypeNode([ - typ, - factory.createLiteralTypeNode(factory.createNull()), - ]); -} - -export function preamble(queries: Query[]) { - const imports: Node[] = [ - factory.createImportDeclaration( - undefined, - factory.createImportClause( - false, - undefined, - factory.createNamedImports([ - factory.createImportSpecifier( - false, - undefined, - factory.createIdentifier("QueryArrayConfig") - ), - factory.createImportSpecifier( - false, - undefined, - factory.createIdentifier("QueryArrayResult") - ), - ]) - ), - factory.createStringLiteral("pg"), - undefined - ), - ]; - - const hasInterval = queries.some( - (query) => - query.params.some((p) => p.column?.type?.name === "interval") || - query.columns.some((c) => c.type?.name === "interval") - ); - - if (hasInterval) { - imports.push( - factory.createImportDeclaration( - undefined, - factory.createImportClause( - false, - undefined, - factory.createNamedImports([ - factory.createImportSpecifier( - false, - undefined, - factory.createIdentifier("IPostgresInterval") - ), - ]) - ), - factory.createStringLiteral("postgres-interval"), - undefined - ) - ); - } - - imports.push( - factory.createInterfaceDeclaration( - undefined, - factory.createIdentifier("Client"), - undefined, - undefined, - [ - factory.createPropertySignature( - undefined, - factory.createIdentifier("query"), - undefined, - factory.createFunctionTypeNode( - undefined, - [ - factory.createParameterDeclaration( - undefined, - undefined, - factory.createIdentifier("config"), - undefined, - factory.createTypeReferenceNode( - factory.createIdentifier("QueryArrayConfig"), - undefined - ), - undefined - ), - ], - factory.createTypeReferenceNode( - factory.createIdentifier("Promise"), - [ - factory.createTypeReferenceNode( - factory.createIdentifier("QueryArrayResult"), - undefined - ), - ] - ) - ) - ), - ] - ) - ); - - return imports; -} - function funcParamsDecl(iface: string | undefined, params: Parameter[]) { let funcParams = [ factory.createParameterDeclaration( @@ -423,375 +44,754 @@ function funcParamsDecl(iface: string | undefined, params: Parameter[]) { return funcParams; } -export function execDecl( - funcName: string, - queryName: string, - argIface: string | undefined, - params: Parameter[] -) { - const funcParams = funcParamsDecl(argIface, params); +export class Driver { + columnType(column?: Column): TypeNode { + if (column === undefined || column.type === undefined) { + return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); + } + // Some of the type names have the `pgcatalog.` prefix. Remove this. + let typeName = column.type.name; + const pgCatalog = "pg_catalog."; + if (typeName.startsWith(pgCatalog)) { + typeName = typeName.slice(pgCatalog.length); + } + let typ: TypeNode = factory.createKeywordTypeNode(SyntaxKind.StringKeyword); + switch (typeName) { + case "aclitem": { + // string + break; + } + case "bigserial": { + // string + break; + } + case "bit": { + // string + break; + } + case "bool": { + typ = factory.createKeywordTypeNode(SyntaxKind.BooleanKeyword); + break; + } + case "box": { + // string + break; + } + case "bpchar": { + // string + break; + } + case "bytea": { + // TODO: Is this correct or node-specific? + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Buffer"), + undefined + ); + break; + } + case "cid": { + // string + break; + } + case "cidr": { + // string + break; + } + case "circle": { + typ = factory.createTypeLiteralNode([ + factory.createPropertySignature( + undefined, + factory.createIdentifier("x"), + undefined, + factory.createKeywordTypeNode(SyntaxKind.NumberKeyword) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier("y"), + undefined, + factory.createKeywordTypeNode(SyntaxKind.NumberKeyword) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier("radius"), + undefined, + factory.createKeywordTypeNode(SyntaxKind.NumberKeyword) + ), + ]); + break; + } + case "date": { + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Date"), + undefined + ); + break; + } + case "float4": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "float8": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "inet": { + // string + break; + } + case "int2": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "int4": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "int8": { + // string + break; + } + case "interval": { + typ = factory.createTypeReferenceNode( + factory.createIdentifier("IPostgresInterval"), + undefined + ); + break; + } + case "json": { + typ = factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); + break; + } + case "jsonb": { + typ = factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); + break; + } + case "line": { + // string + break; + } + case "lseg": { + // string + break; + } + case "madaddr": { + // string + break; + } + case "madaddr8": { + // string + break; + } + case "money": { + // string + break; + } + case "oid": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "path": { + // string + break; + } + case "pg_node_tree": { + // string + break; + } + case "pg_snapshot": { + // string + break; + } + case "point": { + typ = factory.createTypeLiteralNode([ + factory.createPropertySignature( + undefined, + factory.createIdentifier("x"), + undefined, + factory.createKeywordTypeNode(SyntaxKind.NumberKeyword) + ), + factory.createPropertySignature( + undefined, + factory.createIdentifier("y"), + undefined, + factory.createKeywordTypeNode(SyntaxKind.NumberKeyword) + ), + ]); + break; + } + case "polygon": { + // string + break; + } + case "regproc": { + // string + break; + } + case "regrole": { + // string + break; + } + case "serial": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "serial2": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "serial4": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "serial8": { + // string + break; + } + case "smallserial": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "tid": { + // string + break; + } + case "text": { + // string + break; + } + case "time": { + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Date"), + undefined + ); + break; + } + case "timetz": { + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Date"), + undefined + ); + break; + } + case "timestamp": { + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Date"), + undefined + ); + break; + } + case "timestamptz": { + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Date"), + undefined + ); + break; + } + case "tsquery": { + // string + break; + } + case "tsvector": { + // string + break; + } + case "txid_snapshot": { + // string + break; + } + case "uuid": { + // string + break; + } + case "varbit": { + // string + break; + } + case "varchar": { + // string + break; + } + case "xid": { + // string + break; + } + case "xml": { + // string + break; + } + } + if (column.isArray || column.arrayDims > 0) { + let dims = Math.max(column.arrayDims || 1); + for (let i = 0; i < dims; i++) { + typ = factory.createArrayTypeNode(typ); + } + } + if (column.notNull) { + return typ; + } + return factory.createUnionTypeNode([ + typ, + factory.createLiteralTypeNode(factory.createNull()), + ]); + } - return factory.createFunctionDeclaration( - [ - factory.createToken(SyntaxKind.ExportKeyword), - factory.createToken(SyntaxKind.AsyncKeyword), - ], - undefined, - factory.createIdentifier(funcName), - undefined, - funcParams, - factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ - factory.createKeywordTypeNode(SyntaxKind.VoidKeyword), - ]), - factory.createBlock( - [ - factory.createExpressionStatement( - factory.createAwaitExpression( - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier("client"), - factory.createIdentifier("query") + preamble(queries: Query[]) { + const imports: Node[] = [ + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([ + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("QueryArrayConfig") + ), + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("QueryArrayResult") + ), + ]) + ), + factory.createStringLiteral("pg"), + undefined + ), + ]; + + const hasInterval = queries.some( + (query) => + query.params.some((p) => p.column?.type?.name === "interval") || + query.columns.some((c) => c.type?.name === "interval") + ); + + if (hasInterval) { + imports.push( + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([ + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("IPostgresInterval") ), + ]) + ), + factory.createStringLiteral("postgres-interval"), + undefined + ) + ); + } + + imports.push( + factory.createInterfaceDeclaration( + undefined, + factory.createIdentifier("Client"), + undefined, + undefined, + [ + factory.createPropertySignature( + undefined, + factory.createIdentifier("query"), + undefined, + factory.createFunctionTypeNode( undefined, [ - factory.createObjectLiteralExpression( - [ - factory.createPropertyAssignment( - factory.createIdentifier("text"), - factory.createIdentifier(queryName) - ), - factory.createPropertyAssignment( - factory.createIdentifier("values"), - factory.createArrayLiteralExpression( - params.map((param, i) => - factory.createPropertyAccessExpression( - factory.createIdentifier("args"), - factory.createIdentifier(argName(i, param.column)) - ) - ), - false - ) - ), - factory.createPropertyAssignment( - factory.createIdentifier("rowMode"), - factory.createStringLiteral("array") - ), - ], - true + factory.createParameterDeclaration( + undefined, + undefined, + factory.createIdentifier("config"), + undefined, + factory.createTypeReferenceNode( + factory.createIdentifier("QueryArrayConfig"), + undefined + ), + undefined ), - ] + ], + factory.createTypeReferenceNode( + factory.createIdentifier("Promise"), + [ + factory.createTypeReferenceNode( + factory.createIdentifier("QueryArrayResult"), + undefined + ), + ] + ) ) - ) - ), - ], - true - ) - ); -} + ), + ] + ) + ); -export function oneDecl( - funcName: string, - queryName: string, - argIface: string | undefined, - returnIface: string, - params: Parameter[], - columns: Column[] -) { - const funcParams = funcParamsDecl(argIface, params); + return imports; + } - return factory.createFunctionDeclaration( - [ - factory.createToken(SyntaxKind.ExportKeyword), - factory.createToken(SyntaxKind.AsyncKeyword), - ], - undefined, - factory.createIdentifier(funcName), - undefined, - funcParams, - factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ - factory.createUnionTypeNode([ - factory.createTypeReferenceNode( - factory.createIdentifier(returnIface), - undefined - ), - factory.createLiteralTypeNode(factory.createNull()), - ]), - ]), - factory.createBlock( + execDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + params: Parameter[] + ) { + const funcParams = funcParamsDecl(argIface, params); + + return factory.createFunctionDeclaration( [ - factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - factory.createIdentifier("result"), - undefined, + factory.createToken(SyntaxKind.ExportKeyword), + factory.createToken(SyntaxKind.AsyncKeyword), + ], + undefined, + factory.createIdentifier(funcName), + undefined, + funcParams, + factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ + factory.createKeywordTypeNode(SyntaxKind.VoidKeyword), + ]), + factory.createBlock( + [ + factory.createExpressionStatement( + factory.createAwaitExpression( + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("client"), + factory.createIdentifier("query") + ), undefined, - factory.createAwaitExpression( - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier("client"), - factory.createIdentifier("query") - ), - undefined, + [ + factory.createObjectLiteralExpression( [ - factory.createObjectLiteralExpression( - [ - factory.createPropertyAssignment( - factory.createIdentifier("text"), - factory.createIdentifier(queryName) - ), - factory.createPropertyAssignment( - factory.createIdentifier("values"), - factory.createArrayLiteralExpression( - params.map((param, i) => - factory.createPropertyAccessExpression( - factory.createIdentifier("args"), - factory.createIdentifier( - argName(i, param.column) - ) - ) - ), - false + factory.createPropertyAssignment( + factory.createIdentifier("text"), + factory.createIdentifier(queryName) + ), + factory.createPropertyAssignment( + factory.createIdentifier("values"), + factory.createArrayLiteralExpression( + params.map((param, i) => + factory.createPropertyAccessExpression( + factory.createIdentifier("args"), + factory.createIdentifier(argName(i, param.column)) ) ), - factory.createPropertyAssignment( - factory.createIdentifier("rowMode"), - factory.createStringLiteral("array") - ), - ], - true + false + ) + ), + factory.createPropertyAssignment( + factory.createIdentifier("rowMode"), + factory.createStringLiteral("array") ), - ] + ], + true + ), + ] + ) + ) + ), + ], + true + ) + ); + } + + oneDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + returnIface: string, + params: Parameter[], + columns: Column[] + ) { + const funcParams = funcParamsDecl(argIface, params); + + return factory.createFunctionDeclaration( + [ + factory.createToken(SyntaxKind.ExportKeyword), + factory.createToken(SyntaxKind.AsyncKeyword), + ], + undefined, + factory.createIdentifier(funcName), + undefined, + funcParams, + factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ + factory.createUnionTypeNode([ + factory.createTypeReferenceNode( + factory.createIdentifier(returnIface), + undefined + ), + factory.createLiteralTypeNode(factory.createNull()), + ]), + ]), + factory.createBlock( + [ + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createIdentifier("result"), + undefined, + undefined, + factory.createAwaitExpression( + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("client"), + factory.createIdentifier("query") + ), + undefined, + [ + factory.createObjectLiteralExpression( + [ + factory.createPropertyAssignment( + factory.createIdentifier("text"), + factory.createIdentifier(queryName) + ), + factory.createPropertyAssignment( + factory.createIdentifier("values"), + factory.createArrayLiteralExpression( + params.map((param, i) => + factory.createPropertyAccessExpression( + factory.createIdentifier("args"), + factory.createIdentifier( + argName(i, param.column) + ) + ) + ), + false + ) + ), + factory.createPropertyAssignment( + factory.createIdentifier("rowMode"), + factory.createStringLiteral("array") + ), + ], + true + ), + ] + ) ) - ) - ), - ], - NodeFlags.Const | - // ts.NodeFlags.Constant | - NodeFlags.AwaitContext | - // ts.NodeFlags.Constant | - NodeFlags.ContextFlags | - NodeFlags.TypeExcludesFlags - ) - ), - factory.createIfStatement( - factory.createBinaryExpression( - factory.createPropertyAccessExpression( + ), + ], + NodeFlags.Const | + // ts.NodeFlags.Constant | + NodeFlags.AwaitContext | + // ts.NodeFlags.Constant | + NodeFlags.ContextFlags | + NodeFlags.TypeExcludesFlags + ) + ), + factory.createIfStatement( + factory.createBinaryExpression( factory.createPropertyAccessExpression( - factory.createIdentifier("result"), - factory.createIdentifier("rows") + factory.createPropertyAccessExpression( + factory.createIdentifier("result"), + factory.createIdentifier("rows") + ), + factory.createIdentifier("length") ), - factory.createIdentifier("length") + factory.createToken(SyntaxKind.ExclamationEqualsEqualsToken), + factory.createNumericLiteral("1") ), - factory.createToken(SyntaxKind.ExclamationEqualsEqualsToken), - factory.createNumericLiteral("1") - ), - factory.createBlock( - [factory.createReturnStatement(factory.createNull())], - true + factory.createBlock( + [factory.createReturnStatement(factory.createNull())], + true + ), + undefined ), - undefined - ), - factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - factory.createIdentifier("row"), - undefined, - undefined, - factory.createElementAccessExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier("result"), - factory.createIdentifier("rows") - ), - factory.createNumericLiteral("0") - ) - ), - ], - NodeFlags.Const | - // NodeFlags.Constant | - NodeFlags.AwaitContext | - // NodeFlags.Constant | - NodeFlags.ContextFlags | - NodeFlags.TypeExcludesFlags - ) - ), - factory.createReturnStatement( - factory.createObjectLiteralExpression( - columns.map((col, i) => - factory.createPropertyAssignment( - factory.createIdentifier(colName(i, col)), - factory.createElementAccessExpression( + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( factory.createIdentifier("row"), - factory.createNumericLiteral(`${i}`) - ) - ) - ), - true - ) - ), - ], - true - ) - ); -} - -export function manyDecl( - funcName: string, - queryName: string, - argIface: string | undefined, - returnIface: string, - params: Parameter[], - columns: Column[] -) { - const funcParams = funcParamsDecl(argIface, params); - - return factory.createFunctionDeclaration( - [ - factory.createToken(SyntaxKind.ExportKeyword), - factory.createToken(SyntaxKind.AsyncKeyword), - ], - undefined, - factory.createIdentifier(funcName), - undefined, - funcParams, - factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ - factory.createArrayTypeNode( - factory.createTypeReferenceNode( - factory.createIdentifier(returnIface), - undefined - ) - ), - ]), - factory.createBlock( - [ - factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - factory.createIdentifier("result"), - undefined, - undefined, - factory.createAwaitExpression( - factory.createCallExpression( + undefined, + undefined, + factory.createElementAccessExpression( factory.createPropertyAccessExpression( - factory.createIdentifier("client"), - factory.createIdentifier("query") + factory.createIdentifier("result"), + factory.createIdentifier("rows") ), - undefined, - [ - factory.createObjectLiteralExpression( - [ - factory.createPropertyAssignment( - factory.createIdentifier("text"), - factory.createIdentifier(queryName) - ), - factory.createPropertyAssignment( - factory.createIdentifier("values"), - factory.createArrayLiteralExpression( - params.map((param, i) => - factory.createPropertyAccessExpression( - factory.createIdentifier("args"), - factory.createIdentifier( - argName(i, param.column) - ) - ) - ), - false - ) - ), - factory.createPropertyAssignment( - factory.createIdentifier("rowMode"), - factory.createStringLiteral("array") - ), - ], - true - ), - ] + factory.createNumericLiteral("0") + ) + ), + ], + NodeFlags.Const | + // NodeFlags.Constant | + NodeFlags.AwaitContext | + // NodeFlags.Constant | + NodeFlags.ContextFlags | + NodeFlags.TypeExcludesFlags + ) + ), + factory.createReturnStatement( + factory.createObjectLiteralExpression( + columns.map((col, i) => + factory.createPropertyAssignment( + factory.createIdentifier(colName(i, col)), + factory.createElementAccessExpression( + factory.createIdentifier("row"), + factory.createNumericLiteral(`${i}`) ) ) ), - ], - NodeFlags.Const | - // NodeFlags.Constant | - NodeFlags.AwaitContext | - // NodeFlags.Constant | - NodeFlags.ContextFlags | - NodeFlags.TypeExcludesFlags + true + ) + ), + ], + true + ) + ); + } + + manyDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + returnIface: string, + params: Parameter[], + columns: Column[] + ) { + const funcParams = funcParamsDecl(argIface, params); + + return factory.createFunctionDeclaration( + [ + factory.createToken(SyntaxKind.ExportKeyword), + factory.createToken(SyntaxKind.AsyncKeyword), + ], + undefined, + factory.createIdentifier(funcName), + undefined, + funcParams, + factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ + factory.createArrayTypeNode( + factory.createTypeReferenceNode( + factory.createIdentifier(returnIface), + undefined ) ), - factory.createReturnStatement( - factory.createCallExpression( - factory.createPropertyAccessExpression( + ]), + factory.createBlock( + [ + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createIdentifier("result"), + undefined, + undefined, + factory.createAwaitExpression( + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("client"), + factory.createIdentifier("query") + ), + undefined, + [ + factory.createObjectLiteralExpression( + [ + factory.createPropertyAssignment( + factory.createIdentifier("text"), + factory.createIdentifier(queryName) + ), + factory.createPropertyAssignment( + factory.createIdentifier("values"), + factory.createArrayLiteralExpression( + params.map((param, i) => + factory.createPropertyAccessExpression( + factory.createIdentifier("args"), + factory.createIdentifier( + argName(i, param.column) + ) + ) + ), + false + ) + ), + factory.createPropertyAssignment( + factory.createIdentifier("rowMode"), + factory.createStringLiteral("array") + ), + ], + true + ), + ] + ) + ) + ), + ], + NodeFlags.Const | + // NodeFlags.Constant | + NodeFlags.AwaitContext | + // NodeFlags.Constant | + NodeFlags.ContextFlags | + NodeFlags.TypeExcludesFlags + ) + ), + factory.createReturnStatement( + factory.createCallExpression( factory.createPropertyAccessExpression( - factory.createIdentifier("result"), - factory.createIdentifier("rows") + factory.createPropertyAccessExpression( + factory.createIdentifier("result"), + factory.createIdentifier("rows") + ), + factory.createIdentifier("map") ), - factory.createIdentifier("map") - ), - undefined, - [ - factory.createArrowFunction( - undefined, - undefined, - [ - factory.createParameterDeclaration( - undefined, - undefined, - factory.createIdentifier("row"), - undefined, - undefined, - undefined - ), - ], - undefined, - factory.createToken(SyntaxKind.EqualsGreaterThanToken), - factory.createBlock( + undefined, + [ + factory.createArrowFunction( + undefined, + undefined, [ - factory.createReturnStatement( - factory.createObjectLiteralExpression( - columns.map((col, i) => - factory.createPropertyAssignment( - factory.createIdentifier(colName(i, col)), - factory.createElementAccessExpression( - factory.createIdentifier("row"), - factory.createNumericLiteral(`${i}`) - ) - ) - ), - true - ) + factory.createParameterDeclaration( + undefined, + undefined, + factory.createIdentifier("row"), + undefined, + undefined, + undefined ), ], - true - ) - ), - ] - ) - ), - ], - true - ) - ); -} + undefined, + factory.createToken(SyntaxKind.EqualsGreaterThanToken), + factory.createBlock( + [ + factory.createReturnStatement( + factory.createObjectLiteralExpression( + columns.map((col, i) => + factory.createPropertyAssignment( + factory.createIdentifier(colName(i, col)), + factory.createElementAccessExpression( + factory.createIdentifier("row"), + factory.createNumericLiteral(`${i}`) + ) + ) + ), + true + ) + ), + ], + true + ) + ), + ] + ) + ), + ], + true + ) + ); + } -export function execlastidDecl( - funcName: string, - queryName: string, - argIface: string | undefined, - params: Parameter[] -): FunctionDeclaration { - throw new Error('pg driver currently does not support :execlastid') + execlastidDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + params: Parameter[] + ): FunctionDeclaration { + throw new Error("pg driver currently does not support :execlastid"); + } } - -export default { - columnType, - execDecl, - manyDecl, - oneDecl, - preamble, - execlastidDecl, -}; diff --git a/src/drivers/postgres.ts b/src/drivers/postgres.ts index f9d7bc8..68d23f2 100644 --- a/src/drivers/postgres.ts +++ b/src/drivers/postgres.ts @@ -1,283 +1,15 @@ -import { SyntaxKind, NodeFlags, TypeNode, factory, FunctionDeclaration } from "typescript"; +import { + SyntaxKind, + NodeFlags, + TypeNode, + factory, + FunctionDeclaration, +} from "typescript"; import { Parameter, Column } from "../gen/plugin/codegen_pb"; import { argName, colName } from "./utlis"; import { log } from "../logger"; -export function columnType(column?: Column): TypeNode { - if (column === undefined || column.type === undefined) { - return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); - } - // Some of the type names have the `pgcatalog.` prefix. Remove this. - let typeName = column.type.name; - const pgCatalog = "pg_catalog."; - if (typeName.startsWith(pgCatalog)) { - typeName = typeName.slice(pgCatalog.length); - } - let typ: TypeNode = factory.createKeywordTypeNode(SyntaxKind.StringKeyword); - switch (typeName) { - case "aclitem": { - // string - break; - } - case "bigserial": { - // string - break; - } - case "bit": { - // string - break; - } - case "bool": { - typ = factory.createKeywordTypeNode(SyntaxKind.BooleanKeyword); - break; - } - case "box": { - // string - break; - } - case "bpchar": { - // string - break; - } - case "bytea": { - // TODO: Is this correct or node-specific? - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Buffer"), - undefined - ); - break; - } - case "cid": { - // string - break; - } - case "cidr": { - // string - break; - } - case "circle": { - // string - break; - } - case "date": { - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Date"), - undefined - ); - break; - } - case "float4": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "float8": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "inet": { - // string - break; - } - case "int2": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "int4": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "int8": { - // string - break; - } - case "interval": { - // string - break; - } - case "json": { - typ = factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); - break; - } - case "jsonb": { - typ = factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); - break; - } - case "line": { - // string - break; - } - case "lseg": { - // string - break; - } - case "madaddr": { - // string - break; - } - case "madaddr8": { - // string - break; - } - case "money": { - // string - break; - } - case "oid": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "path": { - // string - break; - } - case "pg_node_tree": { - // string - break; - } - case "pg_snapshot": { - // string - break; - } - case "point": { - // string - break; - } - case "polygon": { - // string - break; - } - case "regproc": { - // string - break; - } - case "regrole": { - // string - break; - } - case "serial": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "serial2": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "serial4": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "serial8": { - // string - break; - } - case "smallserial": { - typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); - break; - } - case "tid": { - // string - break; - } - case "text": { - // string - break; - } - case "time": { - // string - break; - } - case "timetz": { - // string - break; - } - case "timestamp": { - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Date"), - undefined - ); - break; - } - case "timestamptz": { - typ = factory.createTypeReferenceNode( - factory.createIdentifier("Date"), - undefined - ); - break; - } - case "tsquery": { - // string - break; - } - case "tsvector": { - // string - break; - } - case "txid_snapshot": { - // string - break; - } - case "uuid": { - // string - break; - } - case "varbit": { - // string - break; - } - case "varchar": { - // string - break; - } - case "xid": { - // string - break; - } - case "xml": { - // string - break; - } - default: { - log(`unknown type ${column.type?.name}`); - break; - } - } - if (column.isArray || column.arrayDims > 0) { - let dims = Math.max(column.arrayDims || 1); - for (let i = 0; i < dims; i++) { - typ = factory.createArrayTypeNode(typ); - } - } - if (column.notNull) { - return typ; - } - return factory.createUnionTypeNode([ - typ, - factory.createLiteralTypeNode(factory.createNull()), - ]); -} - -export function preamble(queries: unknown) { - return [ - factory.createImportDeclaration( - undefined, - factory.createImportClause( - false, - undefined, - factory.createNamedImports([ - factory.createImportSpecifier( - false, - undefined, - factory.createIdentifier("Sql") - ), - ]) - ), - factory.createStringLiteral("postgres"), - undefined - ), - ]; -} - function funcParamsDecl(iface: string | undefined, params: Parameter[]) { let funcParams = [ factory.createParameterDeclaration( @@ -312,193 +44,364 @@ function funcParamsDecl(iface: string | undefined, params: Parameter[]) { return funcParams; } -export function execDecl( - funcName: string, - queryName: string, - argIface: string | undefined, - params: Parameter[] -) { - const funcParams = funcParamsDecl(argIface, params); +export class Driver { + columnType(column?: Column): TypeNode { + if (column === undefined || column.type === undefined) { + return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); + } + // Some of the type names have the `pgcatalog.` prefix. Remove this. + let typeName = column.type.name; + const pgCatalog = "pg_catalog."; + if (typeName.startsWith(pgCatalog)) { + typeName = typeName.slice(pgCatalog.length); + } + let typ: TypeNode = factory.createKeywordTypeNode(SyntaxKind.StringKeyword); + switch (typeName) { + case "aclitem": { + // string + break; + } + case "bigserial": { + // string + break; + } + case "bit": { + // string + break; + } + case "bool": { + typ = factory.createKeywordTypeNode(SyntaxKind.BooleanKeyword); + break; + } + case "box": { + // string + break; + } + case "bpchar": { + // string + break; + } + case "bytea": { + // TODO: Is this correct or node-specific? + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Buffer"), + undefined + ); + break; + } + case "cid": { + // string + break; + } + case "cidr": { + // string + break; + } + case "circle": { + // string + break; + } + case "date": { + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Date"), + undefined + ); + break; + } + case "float4": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "float8": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "inet": { + // string + break; + } + case "int2": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "int4": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "int8": { + // string + break; + } + case "interval": { + // string + break; + } + case "json": { + typ = factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); + break; + } + case "jsonb": { + typ = factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); + break; + } + case "line": { + // string + break; + } + case "lseg": { + // string + break; + } + case "madaddr": { + // string + break; + } + case "madaddr8": { + // string + break; + } + case "money": { + // string + break; + } + case "oid": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "path": { + // string + break; + } + case "pg_node_tree": { + // string + break; + } + case "pg_snapshot": { + // string + break; + } + case "point": { + // string + break; + } + case "polygon": { + // string + break; + } + case "regproc": { + // string + break; + } + case "regrole": { + // string + break; + } + case "serial": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "serial2": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "serial4": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "serial8": { + // string + break; + } + case "smallserial": { + typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + break; + } + case "tid": { + // string + break; + } + case "text": { + // string + break; + } + case "time": { + // string + break; + } + case "timetz": { + // string + break; + } + case "timestamp": { + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Date"), + undefined + ); + break; + } + case "timestamptz": { + typ = factory.createTypeReferenceNode( + factory.createIdentifier("Date"), + undefined + ); + break; + } + case "tsquery": { + // string + break; + } + case "tsvector": { + // string + break; + } + case "txid_snapshot": { + // string + break; + } + case "uuid": { + // string + break; + } + case "varbit": { + // string + break; + } + case "varchar": { + // string + break; + } + case "xid": { + // string + break; + } + case "xml": { + // string + break; + } + default: { + log(`unknown type ${column.type?.name}`); + break; + } + } + if (column.isArray || column.arrayDims > 0) { + let dims = Math.max(column.arrayDims || 1); + for (let i = 0; i < dims; i++) { + typ = factory.createArrayTypeNode(typ); + } + } + if (column.notNull) { + return typ; + } + return factory.createUnionTypeNode([ + typ, + factory.createLiteralTypeNode(factory.createNull()), + ]); + } - return factory.createFunctionDeclaration( - [ - factory.createToken(SyntaxKind.ExportKeyword), - factory.createToken(SyntaxKind.AsyncKeyword), - ], - undefined, - factory.createIdentifier(funcName), - undefined, - funcParams, - factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ - factory.createKeywordTypeNode(SyntaxKind.VoidKeyword), - ]), - factory.createBlock( - [ - factory.createExpressionStatement( - factory.createAwaitExpression( - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier("sql"), - factory.createIdentifier("unsafe") - ), + preamble(queries: unknown) { + return [ + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports([ + factory.createImportSpecifier( + false, undefined, - [ - factory.createIdentifier(queryName), - factory.createArrayLiteralExpression( - params.map((param, i) => - factory.createPropertyAccessExpression( - factory.createIdentifier("args"), - factory.createIdentifier(argName(i, param.column)) - ) - ), - false - ), - ] - ) - ) + factory.createIdentifier("Sql") + ), + ]) ), - ], - true - ) - ); -} + factory.createStringLiteral("postgres"), + undefined + ), + ]; + } -export function manyDecl( - funcName: string, - queryName: string, - argIface: string | undefined, - returnIface: string, - params: Parameter[], - columns: Column[] -) { - const funcParams = funcParamsDecl(argIface, params); + execDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + params: Parameter[] + ) { + const funcParams = funcParamsDecl(argIface, params); - return factory.createFunctionDeclaration( - [ - factory.createToken(SyntaxKind.ExportKeyword), - factory.createToken(SyntaxKind.AsyncKeyword), - ], - undefined, - factory.createIdentifier(funcName), - undefined, - funcParams, - factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ - factory.createArrayTypeNode( - factory.createTypeReferenceNode( - factory.createIdentifier(returnIface), - undefined - ) - ), - ]), - factory.createBlock( + return factory.createFunctionDeclaration( [ - factory.createReturnStatement( - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createAwaitExpression( - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier("sql"), - factory.createIdentifier("unsafe") - ), - undefined, - [ - factory.createIdentifier(queryName), - factory.createArrayLiteralExpression( - params.map((param, i) => - factory.createPropertyAccessExpression( - factory.createIdentifier("args"), - factory.createIdentifier(argName(i, param.column)) - ) - ), - false - ), - ] - ), - factory.createIdentifier("values") - ), - undefined, - undefined - ) - ), - factory.createIdentifier("map") - ), - undefined, - [ - factory.createArrowFunction( - undefined, + factory.createToken(SyntaxKind.ExportKeyword), + factory.createToken(SyntaxKind.AsyncKeyword), + ], + undefined, + factory.createIdentifier(funcName), + undefined, + funcParams, + factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ + factory.createKeywordTypeNode(SyntaxKind.VoidKeyword), + ]), + factory.createBlock( + [ + factory.createExpressionStatement( + factory.createAwaitExpression( + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("sql"), + factory.createIdentifier("unsafe") + ), undefined, [ - factory.createParameterDeclaration( - undefined, - undefined, - "row" - ), - ], - undefined, - factory.createToken(SyntaxKind.EqualsGreaterThanToken), - factory.createObjectLiteralExpression( - columns.map((col, i) => - factory.createPropertyAssignment( - factory.createIdentifier(colName(i, col)), - factory.createElementAccessExpression( - factory.createIdentifier("row"), - factory.createNumericLiteral(`${i}`) + factory.createIdentifier(queryName), + factory.createArrayLiteralExpression( + params.map((param, i) => + factory.createPropertyAccessExpression( + factory.createIdentifier("args"), + factory.createIdentifier(argName(i, param.column)) ) - ) + ), + false ), - true - ) - ), - ] - ) - ), - ], - true - ) - ); -} + ] + ) + ) + ), + ], + true + ) + ); + } -export function oneDecl( - funcName: string, - queryName: string, - argIface: string | undefined, - returnIface: string, - params: Parameter[], - columns: Column[] -) { - const funcParams = funcParamsDecl(argIface, params); + manyDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + returnIface: string, + params: Parameter[], + columns: Column[] + ) { + const funcParams = funcParamsDecl(argIface, params); - return factory.createFunctionDeclaration( - [ - factory.createToken(SyntaxKind.ExportKeyword), - factory.createToken(SyntaxKind.AsyncKeyword), - ], - undefined, - factory.createIdentifier(funcName), - undefined, - funcParams, - factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ - factory.createUnionTypeNode([ - factory.createTypeReferenceNode( - factory.createIdentifier(returnIface), - undefined + return factory.createFunctionDeclaration( + [ + factory.createToken(SyntaxKind.ExportKeyword), + factory.createToken(SyntaxKind.AsyncKeyword), + ], + undefined, + factory.createIdentifier(funcName), + undefined, + funcParams, + factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ + factory.createArrayTypeNode( + factory.createTypeReferenceNode( + factory.createIdentifier(returnIface), + undefined + ) ), - factory.createLiteralTypeNode(factory.createNull()), ]), - ]), - factory.createBlock( - [ - factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - factory.createIdentifier("rows"), - undefined, - undefined, + factory.createBlock( + [ + factory.createReturnStatement( + factory.createCallExpression( + factory.createPropertyAccessExpression( factory.createAwaitExpression( factory.createCallExpression( factory.createPropertyAccessExpression( @@ -528,94 +431,192 @@ export function oneDecl( undefined, undefined ) - ) + ), + factory.createIdentifier("map") ), - ], - NodeFlags.Const | - // ts.NodeFlags.Constant | - NodeFlags.AwaitContext | - // ts.NodeFlags.Constant | - NodeFlags.ContextFlags | - NodeFlags.TypeExcludesFlags - ) - ), - factory.createIfStatement( - factory.createBinaryExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier("rows"), - factory.createIdentifier("length") - ), - factory.createToken(SyntaxKind.ExclamationEqualsEqualsToken), - factory.createNumericLiteral("1") + undefined, + [ + factory.createArrowFunction( + undefined, + undefined, + [ + factory.createParameterDeclaration( + undefined, + undefined, + "row" + ), + ], + undefined, + factory.createToken(SyntaxKind.EqualsGreaterThanToken), + factory.createObjectLiteralExpression( + columns.map((col, i) => + factory.createPropertyAssignment( + factory.createIdentifier(colName(i, col)), + factory.createElementAccessExpression( + factory.createIdentifier("row"), + factory.createNumericLiteral(`${i}`) + ) + ) + ), + true + ) + ), + ] + ) ), - factory.createBlock( - [factory.createReturnStatement(factory.createNull())], - true + ], + true + ) + ); + } + + oneDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + returnIface: string, + params: Parameter[], + columns: Column[] + ) { + const funcParams = funcParamsDecl(argIface, params); + + return factory.createFunctionDeclaration( + [ + factory.createToken(SyntaxKind.ExportKeyword), + factory.createToken(SyntaxKind.AsyncKeyword), + ], + undefined, + factory.createIdentifier(funcName), + undefined, + funcParams, + factory.createTypeReferenceNode(factory.createIdentifier("Promise"), [ + factory.createUnionTypeNode([ + factory.createTypeReferenceNode( + factory.createIdentifier(returnIface), + undefined ), - undefined - ), - factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - "row", - undefined, - undefined, - factory.createElementAccessExpression( + factory.createLiteralTypeNode(factory.createNull()), + ]), + ]), + factory.createBlock( + [ + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( factory.createIdentifier("rows"), - factory.createNumericLiteral("0") - ) + undefined, + undefined, + factory.createAwaitExpression( + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("sql"), + factory.createIdentifier("unsafe") + ), + undefined, + [ + factory.createIdentifier(queryName), + factory.createArrayLiteralExpression( + params.map((param, i) => + factory.createPropertyAccessExpression( + factory.createIdentifier("args"), + factory.createIdentifier( + argName(i, param.column) + ) + ) + ), + false + ), + ] + ), + factory.createIdentifier("values") + ), + undefined, + undefined + ) + ) + ), + ], + NodeFlags.Const | + // ts.NodeFlags.Constant | + NodeFlags.AwaitContext | + // ts.NodeFlags.Constant | + NodeFlags.ContextFlags | + NodeFlags.TypeExcludesFlags + ) + ), + factory.createIfStatement( + factory.createBinaryExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("rows"), + factory.createIdentifier("length") ), - ], - NodeFlags.Const - ) - ), - factory.createIfStatement( - factory.createPrefixUnaryExpression( - SyntaxKind.ExclamationToken, - factory.createIdentifier("row") + factory.createToken(SyntaxKind.ExclamationEqualsEqualsToken), + factory.createNumericLiteral("1") + ), + factory.createBlock( + [factory.createReturnStatement(factory.createNull())], + true + ), + undefined ), - factory.createBlock( - [factory.createReturnStatement(factory.createNull())], - true + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + "row", + undefined, + undefined, + factory.createElementAccessExpression( + factory.createIdentifier("rows"), + factory.createNumericLiteral("0") + ) + ), + ], + NodeFlags.Const + ) ), - undefined - ), - factory.createReturnStatement( - factory.createObjectLiteralExpression( - columns.map((col, i) => - factory.createPropertyAssignment( - factory.createIdentifier(colName(i, col)), - factory.createElementAccessExpression( - factory.createIdentifier("row"), - factory.createNumericLiteral(`${i}`) - ) - ) + factory.createIfStatement( + factory.createPrefixUnaryExpression( + SyntaxKind.ExclamationToken, + factory.createIdentifier("row") ), - true - ) - ), - ], - true - ) - ); -} + factory.createBlock( + [factory.createReturnStatement(factory.createNull())], + true + ), + undefined + ), + factory.createReturnStatement( + factory.createObjectLiteralExpression( + columns.map((col, i) => + factory.createPropertyAssignment( + factory.createIdentifier(colName(i, col)), + factory.createElementAccessExpression( + factory.createIdentifier("row"), + factory.createNumericLiteral(`${i}`) + ) + ) + ), + true + ) + ), + ], + true + ) + ); + } -export function execlastidDecl( - funcName: string, - queryName: string, - argIface: string | undefined, - params: Parameter[] -): FunctionDeclaration { - throw new Error('postgres driver currently does not support :execlastid') + execlastidDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + params: Parameter[] + ): FunctionDeclaration { + throw new Error("postgres driver currently does not support :execlastid"); + } } - -export default { - columnType, - preamble, - execDecl, - manyDecl, - oneDecl, - execlastidDecl -}; From 570f4e3edca4c88c2fb2adf7bb4c0e2f5cb6b341 Mon Sep 17 00:00:00 2001 From: Kyle Gray Date: Sat, 2 Mar 2024 20:46:58 -0800 Subject: [PATCH 09/12] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 855f342..43f5be8 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ sql: - PostgreSQL via [pg](https://www.npmjs.com/package/pg) or [postgres](https://www.npmjs.com/package/postgres). - MySQL via [mysql2](https://www.npmjs.com/package/mysql2). +- SQLite via [sqlite3](https://www.npmjs.com/package/better-sqlite3). ## Getting started From 1a42ab0ef40a8ed47e379fa7677fcedc5fe92150 Mon Sep 17 00:00:00 2001 From: Yasuhiro SHIMIZU Date: Mon, 4 Mar 2024 06:56:37 +0900 Subject: [PATCH 10/12] feat(mysql): Support string bignumbers in mysql (#16) --- examples/node-mysql2/src/db/query_sql.ts | 12 +++++------ examples/node-mysql2/src/main.ts | 2 ++ examples/sqlc.dev.yaml | 3 +++ src/app.ts | 27 ++++++++++++------------ src/drivers/mysql2.ts | 23 ++++++++++++++++++++ 5 files changed, 47 insertions(+), 20 deletions(-) diff --git a/examples/node-mysql2/src/db/query_sql.ts b/examples/node-mysql2/src/db/query_sql.ts index 1673007..c79988f 100644 --- a/examples/node-mysql2/src/db/query_sql.ts +++ b/examples/node-mysql2/src/db/query_sql.ts @@ -9,11 +9,11 @@ SELECT id, name, bio FROM authors WHERE id = ? LIMIT 1`; export interface GetAuthorArgs { - id: number; + id: string; } export interface GetAuthorRow { - id: number; + id: string; name: string; bio: string | null; } @@ -40,7 +40,7 @@ SELECT id, name, bio FROM authors ORDER BY name`; export interface ListAuthorsRow { - id: number; + id: string; name: string; bio: string | null; } @@ -104,7 +104,7 @@ DELETE FROM authors WHERE id = ?`; export interface DeleteAuthorArgs { - id: number; + id: string; } export async function deleteAuthor(client: Client, args: DeleteAuthorArgs): Promise { @@ -127,8 +127,8 @@ export interface TestRow { cMediumint: number | null; cInt: number | null; cInteger: number | null; - cBigint: number | null; - cSerial: number; + cBigint: string | null; + cSerial: string; cDecimal: string | null; cDec: string | null; cNumeric: string | null; diff --git a/examples/node-mysql2/src/main.ts b/examples/node-mysql2/src/main.ts index fcf7596..d8576e3 100644 --- a/examples/node-mysql2/src/main.ts +++ b/examples/node-mysql2/src/main.ts @@ -22,6 +22,8 @@ async function main() { user: url.username, password: url.password, database: url.pathname.substring(1), + supportBigNumbers: true, + bigNumberStrings: true, ssl: { // TODO: FIXME rejectUnauthorized: false, diff --git a/examples/sqlc.dev.yaml b/examples/sqlc.dev.yaml index d2a69a0..f622ec1 100644 --- a/examples/sqlc.dev.yaml +++ b/examples/sqlc.dev.yaml @@ -49,6 +49,9 @@ sql: options: runtime: node driver: mysql2 + mysql2: + big_number_strings: true + support_big_numbers: true - schema: "authors/mysql/schema.sql" queries: "authors/mysql/query.sql" engine: "mysql" diff --git a/src/app.ts b/src/app.ts index e1dec6f..e3a077c 100644 --- a/src/app.ts +++ b/src/app.ts @@ -31,7 +31,7 @@ import { argName, colName } from "./drivers/utlis"; import { Driver as Sqlite3Driver } from "./drivers/better-sqlite3"; import { Driver as PgDriver } from "./drivers/pg"; import { Driver as PostgresDriver } from "./drivers/postgres"; -import { Driver as MysqlDriver } from "./drivers/mysql2"; +import { Mysql2Options, Driver as MysqlDriver } from "./drivers/mysql2"; // Read input from stdin const input = readInput(); @@ -43,6 +43,7 @@ writeOutput(result); interface Options { runtime?: string; driver?: string; + mysql2?: Mysql2Options } interface Driver { @@ -78,10 +79,10 @@ interface Driver { ) => Node; } -function createNodeGenerator(driver?: string): Driver { - switch (driver) { +function createNodeGenerator(options: Options): Driver { + switch (options.driver) { case "mysql2": { - return new MysqlDriver(); + return new MysqlDriver(options.mysql2); } case "pg": { return new PgDriver(); @@ -93,7 +94,7 @@ function createNodeGenerator(driver?: string): Driver { return new Sqlite3Driver(); } } - throw new Error(`unknown driver: ${driver}`); + throw new Error(`unknown driver: ${options.driver}`); } function codegen(input: GenerateRequest): GenerateResponse { @@ -105,7 +106,7 @@ function codegen(input: GenerateRequest): GenerateResponse { options = JSON.parse(text) as Options; } - const driver = createNodeGenerator(options.driver); + const driver = createNodeGenerator(options); // TODO: Verify options, parse them from protobuf honestly @@ -146,17 +147,15 @@ ${query.text}` ) ); - const ctype = driver.columnType; - let argIface = undefined; let returnIface = undefined; if (query.params.length > 0) { argIface = `${query.name}Args`; - nodes.push(argsDecl(argIface, ctype, query.params)); + nodes.push(argsDecl(argIface, driver, query.params)); } if (query.columns.length > 0) { returnIface = `${query.name}Row`; - nodes.push(rowDecl(returnIface, ctype, query.columns)); + nodes.push(rowDecl(returnIface, driver, query.columns)); } switch (query.cmd) { @@ -240,7 +239,7 @@ function queryDecl(name: string, sql: string) { function argsDecl( name: string, - ctype: (c?: Column) => TypeNode, + driver: Driver, params: Parameter[] ) { return factory.createInterfaceDeclaration( @@ -253,7 +252,7 @@ function argsDecl( undefined, factory.createIdentifier(argName(i, param.column)), undefined, - ctype(param.column) + driver.columnType(param.column) ) ) ); @@ -261,7 +260,7 @@ function argsDecl( function rowDecl( name: string, - ctype: (c?: Column) => TypeNode, + driver: Driver, columns: Column[] ) { return factory.createInterfaceDeclaration( @@ -274,7 +273,7 @@ function rowDecl( undefined, factory.createIdentifier(colName(i, column)), undefined, - ctype(column) + driver.columnType(column) ) ) ); diff --git a/src/drivers/mysql2.ts b/src/drivers/mysql2.ts index 731db8d..7859ade 100644 --- a/src/drivers/mysql2.ts +++ b/src/drivers/mysql2.ts @@ -5,6 +5,11 @@ import { SyntaxKind, NodeFlags, TypeNode, factory } from "typescript"; import { Parameter, Column, Query } from "../gen/plugin/codegen_pb"; import { argName, colName } from "./utlis"; +export interface Mysql2Options { + support_big_numbers?: boolean; + big_number_strings?: boolean; +} + function funcParamsDecl(iface: string | undefined, params: Parameter[]) { let funcParams = [ factory.createParameterDeclaration( @@ -40,6 +45,12 @@ function funcParamsDecl(iface: string | undefined, params: Parameter[]) { } export class Driver { + private readonly options: Mysql2Options + + constructor(options?: Mysql2Options) { + this.options = options ?? {} + } + columnType(column?: Column): TypeNode { if (column === undefined || column.type === undefined) { return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); @@ -49,6 +60,18 @@ export class Driver { switch (column.type.name) { case "bigint": { typ = factory.createKeywordTypeNode(SyntaxKind.NumberKeyword); + + if (this.options.support_big_numbers) { + if (this.options.big_number_strings) { + typ = factory.createKeywordTypeNode(SyntaxKind.StringKeyword) + } else { + typ = factory.createUnionTypeNode([ + factory.createKeywordTypeNode(SyntaxKind.NumberKeyword), + factory.createKeywordTypeNode(SyntaxKind.StringKeyword) + ]) + } + } + break; } case "binary": { From 54496e9f30842c4ee4c46391564a8c2299cfdc49 Mon Sep 17 00:00:00 2001 From: Woo Wen Jun <88195289+woowenjun99@users.noreply.github.com> Date: Wed, 8 May 2024 01:47:02 +0800 Subject: [PATCH 11/12] Update readme (#26) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 43f5be8..b913bd4 100644 --- a/README.md +++ b/README.md @@ -304,7 +304,7 @@ plugins: sql: - schema: "schema.sql" queries: "query.sql" - engine: postgresql + engine: "mysql" codegen: - out: db plugin: ts From 395a0baf65ec47f189ae033f86d461c7da1431b9 Mon Sep 17 00:00:00 2001 From: Yuki Shindo Date: Tue, 26 Nov 2024 15:22:58 +0900 Subject: [PATCH 12/12] docs: Add local development instructions to README (#39) * docs: add local development instructions to readme * fix readme * add a link to the core sqlc development guide --- README.md | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/README.md b/README.md index b913bd4..5798b73 100644 --- a/README.md +++ b/README.md @@ -333,3 +333,61 @@ sql: runtime: node driver: better-sqlite3 # npm package name ``` + +## Development + +If you want to build and test sqlc-gen-typescript locally, follow these steps: + +1. Clone the repository and install dependencies: + ``` + git clone https://github.com/sqlc-dev/sqlc-gen-typescript.git + cd sqlc-gen-typescript + npm install + ``` + +2. Make your desired changes to the codebase. The main source files are located in the `src` directory. + +3. If you've made changes that require updating dependencies, run: + ``` + npm install + ``` + +4. Build the WASM plugin: +Check the `Makefile` for details. + ``` + make out.js + + # Ensure you have Javy installed and available in your PATH + make examples/plugin.wasm + ``` + +5. To test your local build, create a test project with a `sqlc.yaml` file containing: + + ```yaml + version: '2' + plugins: + - name: ts + wasm: + url: file://{path_to_your_local_wasm_file} + sha256: {sha256_of_your_wasm_file} + sql: + - schema: "schema.sql" + queries: "query.sql" + engine: {your_database_engine} + codegen: + - out: db + plugin: ts + options: + runtime: node + driver: {your_database_driver} + ``` + + Replace the placeholders with appropriate values for your setup. + +6. Run sqlc in your test project to generate TypeScript code using your local plugin build: + ``` + sqlc generate + ``` + +For more details on sqlc development, refer to the sqlc core development guide. This guide provides additional information on setting up and working with sqlc in general, which may be useful for contributors to this project. +https://docs.sqlc.dev/en/latest/guides/development.html \ No newline at end of file