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 9c6084e..57776cd 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -1,13 +1,42 @@ name: examples -on: [push] +on: + push: + branches: + - main + pull_request: 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 +49,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 +106,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 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/README.md b/README.md index c0e6128..5798b73 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" @@ -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 @@ -68,8 +69,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 +256,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 +277,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,12 +299,12 @@ 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" - engine: postgresql + engine: "mysql" codegen: - out: db plugin: ts @@ -311,3 +312,82 @@ 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 +``` + +## 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 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/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/bun-mysql2/src/db/query_sql.ts b/examples/bun-mysql2/src/db/query_sql.ts index 9e70d09..1673007 100644 --- a/examples/bun-mysql2/src/db/query_sql.ts +++ b/examples/bun-mysql2/src/db/query_sql.ts @@ -1,4 +1,6 @@ -import mysql, { RowDataPacket } from "mysql2/promise"; +// Code generated by sqlc. DO NOT EDIT. + +import mysql, { RowDataPacket, ResultSetHeader } from "mysql2/promise"; type Client = mysql.Connection | mysql.Pool; @@ -62,7 +64,7 @@ export const createAuthorQuery = `-- name: CreateAuthor :exec INSERT INTO authors ( name, bio ) VALUES ( - ?, ? + ?, ? )`; export interface CreateAuthorArgs { @@ -77,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/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..8e15f3b 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 @@ -20,6 +22,9 @@ export async function getAuthor(sql: Sql, args: GetAuthorArgs): Promise=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..8996855 --- /dev/null +++ b/examples/node-better-sqlite3/src/db/query_sql.ts @@ -0,0 +1,73 @@ +// Code generated by sqlc. DO NOT EDIT. + +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/node-mysql2/src/db/query_sql.ts b/examples/node-mysql2/src/db/query_sql.ts index 9e70d09..c79988f 100644 --- a/examples/node-mysql2/src/db/query_sql.ts +++ b/examples/node-mysql2/src/db/query_sql.ts @@ -1,4 +1,6 @@ -import mysql, { RowDataPacket } from "mysql2/promise"; +// Code generated by sqlc. DO NOT EDIT. + +import mysql, { RowDataPacket, ResultSetHeader } from "mysql2/promise"; type Client = mysql.Connection | mysql.Pool; @@ -7,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; } @@ -38,7 +40,7 @@ SELECT id, name, bio FROM authors ORDER BY name`; export interface ListAuthorsRow { - id: number; + id: string; name: string; bio: string | null; } @@ -62,7 +64,7 @@ export const createAuthorQuery = `-- name: CreateAuthor :exec INSERT INTO authors ( name, bio ) VALUES ( - ?, ? + ?, ? )`; export interface CreateAuthorArgs { @@ -77,12 +79,32 @@ 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 = ?`; export interface DeleteAuthorArgs { - id: number; + id: string; } export async function deleteAuthor(client: Client, args: DeleteAuthorArgs): Promise { @@ -105,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/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..8e15f3b 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 @@ -20,6 +22,9 @@ export async function getAuthor(sql: Sql, args: GetAuthorArgs): Promise Node; + execlastidDecl: ( + name: string, + text: string, + iface: string | undefined, + params: Parameter[] + ) => Node; manyDecl: ( name: string, text: string, @@ -71,19 +79,22 @@ interface Driver { ) => Node; } -function createNodeGenerator(driver?: string): Driver { - switch (driver) { +function createNodeGenerator(options: Options): Driver { + switch (options.driver) { case "mysql2": { - return mysql2; + return new MysqlDriver(options.mysql2); } case "pg": { - return pg; + return new PgDriver(); } case "postgres": { - return postgres; + return new PostgresDriver(); + } + case "better-sqlite3": { + return new Sqlite3Driver(); } } - throw new Error(`unknown driver: ${driver}`); + throw new Error(`unknown driver: ${options.driver}`); } function codegen(input: GenerateRequest): GenerateResponse { @@ -95,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 @@ -136,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) { @@ -156,6 +165,12 @@ ${query.text}` ); break; } + case ":execlastid": { + nodes.push( + driver.execlastidDecl(lowerName, textName, argIface, query.params) + ); + break; + } case ":one": { nodes.push( driver.oneDecl( @@ -224,7 +239,7 @@ function queryDecl(name: string, sql: string) { function argsDecl( name: string, - ctype: (c?: Column) => TypeNode, + driver: Driver, params: Parameter[] ) { return factory.createInterfaceDeclaration( @@ -237,7 +252,7 @@ function argsDecl( undefined, factory.createIdentifier(argName(i, param.column)), undefined, - ctype(param.column) + driver.columnType(param.column) ) ) ); @@ -245,7 +260,7 @@ function argsDecl( function rowDecl( name: string, - ctype: (c?: Column) => TypeNode, + driver: Driver, columns: Column[] ) { return factory.createInterfaceDeclaration( @@ -258,7 +273,7 @@ function rowDecl( undefined, factory.createIdentifier(colName(i, column)), undefined, - ctype(column) + driver.columnType(column) ) ) ); @@ -274,7 +289,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"; diff --git a/src/drivers/better-sqlite3.ts b/src/drivers/better-sqlite3.ts new file mode 100644 index 0000000..c6210e8 --- /dev/null +++ b/src/drivers/better-sqlite3.ts @@ -0,0 +1,438 @@ +import { + SyntaxKind, + NodeFlags, + Node, + TypeNode, + factory, + FunctionDeclaration, +} from "typescript"; + +import { Parameter, Column, Query } from "../gen/plugin/codegen_pb"; +import { argName } from "./utlis"; + +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 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); + } + + 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.createNamedImports([ + factory.createImportSpecifier( + false, + undefined, + factory.createIdentifier("Database") + ), + ]) + ), + factory.createStringLiteral("better-sqlite3"), + undefined + ), + ]; + + return imports; + } + + 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 + ) + ); + } + + 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 + ) + ); + } + + 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 + ) + ); + } + + execlastidDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + params: Parameter[] + ): FunctionDeclaration { + throw new Error( + "better-sqlite3 driver currently does not support :execlastid" + ); + } +} diff --git a/src/drivers/mysql2.ts b/src/drivers/mysql2.ts index d6f943b..7859ade 100644 --- a/src/drivers/mysql2.ts +++ b/src/drivers/mysql2.ts @@ -2,209 +2,12 @@ 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 { - 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: unknown) { - return [ - factory.createImportDeclaration( - undefined, - factory.createImportClause( - false, - factory.createIdentifier("mysql"), - factory.createNamedImports([ - factory.createImportSpecifier( - false, - undefined, - factory.createIdentifier("RowDataPacket") - ), - ]) - ), - 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 interface Mysql2Options { + support_big_numbers?: boolean; + big_number_strings?: boolean; } function funcParamsDecl(iface: string | undefined, params: Parameter[]) { @@ -241,380 +44,709 @@ 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 { + 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); + } + let typ: TypeNode = factory.createKeywordTypeNode(SyntaxKind.StringKeyword); + + 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": { + 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.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 + ), + ]) + ), + ]; + } + + 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( + return factory.createFunctionDeclaration( [ - factory.createExpressionStatement( - factory.createAwaitExpression( + 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.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 + ), + ] + ) + ) + ), + ], + 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.createBlock( + [ + 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.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 + ), + ] + ) + ) + ), + ], + NodeFlags.Const | + // NodeFlags.Constant | + NodeFlags.AwaitContext | + // NodeFlags.Constant | + NodeFlags.ContextFlags | + NodeFlags.TypeExcludesFlags + ) + ), + factory.createReturnStatement( factory.createCallExpression( factory.createPropertyAccessExpression( - factory.createIdentifier("client"), - factory.createIdentifier("query") + factory.createIdentifier("rows"), + factory.createIdentifier("map") ), undefined, [ - factory.createObjectLiteralExpression( + factory.createArrowFunction( + undefined, + undefined, [ - 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.createParameterDeclaration( + undefined, + undefined, + factory.createIdentifier("row"), + undefined, + undefined, + undefined ), ], - 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 - ) - ); -} + ), + ], + true + ) + ); + } -export function manyDecl( - 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.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, - 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 | - // NodeFlags.Constant | - NodeFlags.AwaitContext | - // NodeFlags.Constant | - NodeFlags.ContextFlags | - NodeFlags.TypeExcludesFlags - ) - ), - factory.createReturnStatement( - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier("rows"), - factory.createIdentifier("map") + factory.createToken(SyntaxKind.ExclamationEqualsEqualsToken), + factory.createNumericLiteral("1") + ), + factory.createBlock( + [factory.createReturnStatement(factory.createNull())], + true ), + undefined + ), + factory.createVariableStatement( undefined, - [ - factory.createArrowFunction( - undefined, - undefined, - [ - factory.createParameterDeclaration( - undefined, - 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 + ) + ), + factory.createReturnStatement( + factory.createObjectLiteralExpression( + columns.map((col, i) => + factory.createPropertyAssignment( + factory.createIdentifier(colName(i, col)), + factory.createElementAccessExpression( 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}`) - ) - ) - ), - true - ) - ), - ], - true + 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); + 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.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.createTypeReferenceNode("number", undefined), + ]), + factory.createBlock( + [ + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createArrayBindingPattern([ + factory.createBindingElement( + undefined, + undefined, + factory.createIdentifier("result"), + undefined ), - [ - factory.createArrayTypeNode( + ]), + undefined, + undefined, + factory.createAwaitExpression( + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier("client"), + factory.createIdentifier("query") + ), + [ factory.createTypeReferenceNode( - factory.createIdentifier("RowDataPacket"), + 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) + ), + ], + [ + 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 + ) + ), + ], + 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") - ), - factory.createToken(SyntaxKind.ExclamationEqualsEqualsToken), - factory.createNumericLiteral("1") - ), - factory.createBlock( - [factory.createReturnStatement(factory.createNull())], - true + ), + ], + 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.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.createObjectLiteralExpression( - columns.map((col, i) => - factory.createPropertyAssignment( - factory.createIdentifier(colName(i, col)), - factory.createElementAccessExpression( - factory.createIdentifier("row"), - factory.createNumericLiteral(`${i}`) - ) - ) - ), - true - ) - ), - ], - true - ) - ); + factory.createToken(SyntaxKind.QuestionQuestionToken), + factory.createNumericLiteral(0) + ) + ), + ], + true + ) + ); + } } - -export default { - columnType, - preamble, - execDecl, - manyDecl, - oneDecl, -}; diff --git a/src/drivers/pg.ts b/src/drivers/pg.ts index db6936d..95828cf 100644 --- a/src/drivers/pg.ts +++ b/src/drivers/pg.ts @@ -1,394 +1,15 @@ -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"; -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,365 +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 default { - columnType, - execDecl, - manyDecl, - oneDecl, - preamble, -}; + execlastidDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + params: Parameter[] + ): FunctionDeclaration { + throw new Error("pg driver currently does not support :execlastid"); + } +} diff --git a/src/drivers/postgres.ts b/src/drivers/postgres.ts index 44c4a47..68d23f2 100644 --- a/src/drivers/postgres.ts +++ b/src/drivers/postgres.ts @@ -1,283 +1,15 @@ -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"; 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,189 +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( @@ -510,7 +417,9 @@ export function oneDecl( params.map((param, i) => factory.createPropertyAccessExpression( factory.createIdentifier("args"), - factory.createIdentifier(argName(i, param.column)) + factory.createIdentifier( + argName(i, param.column) + ) ) ), false @@ -520,67 +429,194 @@ export function oneDecl( factory.createIdentifier("values") ), undefined, - 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") + 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 + ) + ), + ] + ) + ), + ], + 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("rows"), + 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") + ), + 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( + "row", + undefined, + undefined, + factory.createElementAccessExpression( + factory.createIdentifier("rows"), + factory.createNumericLiteral("0") + ) + ), + ], + NodeFlags.Const + ) ), - undefined - ), - factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList([ - factory.createVariableDeclaration("row", undefined, undefined, factory.createElementAccessExpression( - factory.createIdentifier("rows"), - factory.createNumericLiteral("0") - )), - ], NodeFlags.Const) - ), - 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 default { - columnType, - preamble, - execDecl, - manyDecl, - oneDecl, -}; + execlastidDecl( + funcName: string, + queryName: string, + argIface: string | undefined, + params: Parameter[] + ): FunctionDeclaration { + throw new Error("postgres driver currently does not support :execlastid"); + } +}