From 75972110e64dc5de93f71a860cfd7566340b19d5 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Wed, 1 May 2024 19:12:36 +0200 Subject: [PATCH] autoconvert null to options --- CHANGELOG.md | 1 + packages/cli/src/generator.ts | 8 +- .../src/__snapshots__/rescript.test.js.snap | 8 +- .../src/books/BookService__sql.gen.tsx | 120 ++++++++++++++ .../example/src/books/BookService__sql.res | 26 +-- packages/example/src/books/books.sql | 6 + packages/example/src/books/books__sql.gen.tsx | 121 +++++++++----- packages/example/src/books/books__sql.res | 154 ++++++++++++++---- .../src/comments/comments__sql.gen.tsx | 22 ++- .../example/src/comments/comments__sql.res | 20 +-- .../notifications/notifications__sql.gen.tsx | 6 +- .../src/notifications/notifications__sql.res | 4 +- packages/example/src/rescript.test.res | 50 +++++- packages/runtime/src/tag.ts | 5 +- 14 files changed, 420 insertions(+), 131 deletions(-) create mode 100644 packages/example/src/books/BookService__sql.gen.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index abfd2d02..41963fcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # main - Autoinsert trailing commas in embedded SQL blocks. +- BREAKING CHANGE: `Null.t` is no longer emitted, all `null` values are autoconverted to `option`. This gives a much more idiomatic ReScript experience. # 2.4.0 diff --git a/packages/cli/src/generator.ts b/packages/cli/src/generator.ts index 51d86430..423caa78 100644 --- a/packages/cli/src/generator.ts +++ b/packages/cli/src/generator.ts @@ -145,7 +145,7 @@ export async function queryToTypeDeclarations( (addNullability || nullable || nullable == null) && !removeNullability ) { - tsTypeName = 'Null.t<' + tsTypeName + '>'; + tsTypeName = 'option<' + tsTypeName + '>'; } if (addNullability || removeNullability) { @@ -176,7 +176,7 @@ export async function queryToTypeDeclarations( let tsTypeName = types.use(pgTypeName, TypeScope.Parameter); if (!param.required) { - tsTypeName = 'Null.t<' + tsTypeName + '>'; + tsTypeName = tsTypeName; } // Allow optional scalar parameters to be missing from parameters object @@ -289,9 +289,7 @@ async function generateTypedecsFromFile( const typeSource: TypeSource = (query) => getTypes(query, connection); const { queries, events } = - mode === 'res' - ? parseRescriptFile(contents, fileName) - : parseSQLFile(contents); + mode === 'res' ? parseRescriptFile(contents) : parseSQLFile(contents); if (events.length > 0) { prettyPrintEvents(contents, events); if (events.find((e) => 'critical' in e)) { diff --git a/packages/example/src/__snapshots__/rescript.test.js.snap b/packages/example/src/__snapshots__/rescript.test.js.snap index b99c4bfe..bff6c41e 100644 --- a/packages/example/src/__snapshots__/rescript.test.js.snap +++ b/packages/example/src/__snapshots__/rescript.test.js.snap @@ -3,7 +3,7 @@ exports[`insert query with an inline sql comment 1`] = ` Object { "body": "Just a comment", - "book_id": null, + "book_id": undefined, "id": Any, "user_id": 1, } @@ -51,10 +51,10 @@ exports[`select query with join and a parameter override 1`] = ` Array [ Object { "author_id": 2, - "big_int": null, - "categories": null, + "big_int": undefined, + "categories": undefined, "id": 2, - "meta": null, + "meta": undefined, "name": "The Dragons Of Eden", "rank": 4, }, diff --git a/packages/example/src/books/BookService__sql.gen.tsx b/packages/example/src/books/BookService__sql.gen.tsx new file mode 100644 index 00000000..7204fbef --- /dev/null +++ b/packages/example/src/books/BookService__sql.gen.tsx @@ -0,0 +1,120 @@ +/* TypeScript file generated from BookService__sql.res by genType. */ + +/* eslint-disable */ +/* tslint:disable */ + +const BookService__sqlJS = require('./BookService__sql.js'); + +import type {Pg_Client_t as PgTyped_Pg_Client_t} from 'pgtyped-rescript/src/res/PgTyped.gen'; + +import type {t as JSON_t} from './JSON.gen'; + +export type category = "novel" | "science-fiction" | "thriller"; + +export type arrayJSON_t = JSON_t[]; + +export type categoryArray = category[]; + +/** 'FindBookById' parameters type */ +export type findBookByIdParams = { readonly id?: number }; + +/** 'FindBookById' return type */ +export type findBookByIdResult = { + readonly author_id: (undefined | number); + readonly big_int: (undefined | bigint); + readonly categories: (undefined | categoryArray); + readonly id: number; + readonly meta: (undefined | arrayJSON_t); + readonly name: (undefined | string); + readonly rank: (undefined | number) +}; + +/** 'FindBookById' query type */ +export type findBookByIdQuery = { readonly params: findBookByIdParams; readonly result: findBookByIdResult }; + +/** 'BooksByAuthor' parameters type */ +export type booksByAuthorParams = { readonly authorName: string }; + +/** 'BooksByAuthor' return type */ +export type booksByAuthorResult = { + readonly author_id: (undefined | number); + readonly big_int: (undefined | bigint); + readonly categories: (undefined | categoryArray); + readonly id: number; + readonly meta: (undefined | arrayJSON_t); + readonly name: (undefined | string); + readonly rank: (undefined | number) +}; + +/** 'BooksByAuthor' query type */ +export type booksByAuthorQuery = { readonly params: booksByAuthorParams; readonly result: booksByAuthorResult }; + +/** Returns an array of all matched results. */ +export const FindBookById_many: (_1:PgTyped_Pg_Client_t, _2:findBookByIdParams) => Promise = BookService__sqlJS.FindBookById.many as any; + +/** Returns exactly 1 result. Returns `None` if more or less than exactly 1 result is returned. */ +export const FindBookById_one: (_1:PgTyped_Pg_Client_t, _2:findBookByIdParams) => Promise<(undefined | findBookByIdResult)> = BookService__sqlJS.FindBookById.one as any; + +/** Returns exactly 1 result. Returns `Error` (with an optionally provided `errorMessage`) if more or less than exactly 1 result is returned. */ +export const FindBookById_expectOne: (_1:PgTyped_Pg_Client_t, _2:findBookByIdParams, errorMessage:(undefined | string)) => Promise< + { TAG: "Ok"; _0: findBookByIdResult } + | { TAG: "Error"; _0: string }> = BookService__sqlJS.FindBookById.expectOne as any; + +/** Executes the query, but ignores whatever is returned by it. */ +export const FindBookById_execute: (_1:PgTyped_Pg_Client_t, _2:findBookByIdParams) => Promise = BookService__sqlJS.FindBookById.execute as any; + +export const findBookById: (params:findBookByIdParams, client:PgTyped_Pg_Client_t) => Promise = BookService__sqlJS.findBookById as any; + +/** Returns an array of all matched results. */ +export const BooksByAuthor_many: (_1:PgTyped_Pg_Client_t, _2:booksByAuthorParams) => Promise = BookService__sqlJS.BooksByAuthor.many as any; + +/** Returns exactly 1 result. Returns `None` if more or less than exactly 1 result is returned. */ +export const BooksByAuthor_one: (_1:PgTyped_Pg_Client_t, _2:booksByAuthorParams) => Promise<(undefined | booksByAuthorResult)> = BookService__sqlJS.BooksByAuthor.one as any; + +/** Returns exactly 1 result. Returns `Error` (with an optionally provided `errorMessage`) if more or less than exactly 1 result is returned. */ +export const BooksByAuthor_expectOne: (_1:PgTyped_Pg_Client_t, _2:booksByAuthorParams, errorMessage:(undefined | string)) => Promise< + { TAG: "Ok"; _0: booksByAuthorResult } + | { TAG: "Error"; _0: string }> = BookService__sqlJS.BooksByAuthor.expectOne as any; + +/** Executes the query, but ignores whatever is returned by it. */ +export const BooksByAuthor_execute: (_1:PgTyped_Pg_Client_t, _2:booksByAuthorParams) => Promise = BookService__sqlJS.BooksByAuthor.execute as any; + +export const booksByAuthor: (params:booksByAuthorParams, client:PgTyped_Pg_Client_t) => Promise = BookService__sqlJS.booksByAuthor as any; + +export const FindBookById: { + /** Returns exactly 1 result. Returns `Error` (with an optionally provided `errorMessage`) if more or less than exactly 1 result is returned. */ + expectOne: (_1:PgTyped_Pg_Client_t, _2:findBookByIdParams, errorMessage:(undefined | string)) => Promise< + { + TAG: "Ok"; + _0: findBookByIdResult + } + | { + TAG: "Error"; + _0: string + }>; + /** Returns exactly 1 result. Returns `None` if more or less than exactly 1 result is returned. */ + one: (_1:PgTyped_Pg_Client_t, _2:findBookByIdParams) => Promise<(undefined | findBookByIdResult)>; + /** Returns an array of all matched results. */ + many: (_1:PgTyped_Pg_Client_t, _2:findBookByIdParams) => Promise; + /** Executes the query, but ignores whatever is returned by it. */ + execute: (_1:PgTyped_Pg_Client_t, _2:findBookByIdParams) => Promise +} = BookService__sqlJS.FindBookById as any; + +export const BooksByAuthor: { + /** Returns exactly 1 result. Returns `Error` (with an optionally provided `errorMessage`) if more or less than exactly 1 result is returned. */ + expectOne: (_1:PgTyped_Pg_Client_t, _2:booksByAuthorParams, errorMessage:(undefined | string)) => Promise< + { + TAG: "Ok"; + _0: booksByAuthorResult + } + | { + TAG: "Error"; + _0: string + }>; + /** Returns exactly 1 result. Returns `None` if more or less than exactly 1 result is returned. */ + one: (_1:PgTyped_Pg_Client_t, _2:booksByAuthorParams) => Promise<(undefined | booksByAuthorResult)>; + /** Returns an array of all matched results. */ + many: (_1:PgTyped_Pg_Client_t, _2:booksByAuthorParams) => Promise; + /** Executes the query, but ignores whatever is returned by it. */ + execute: (_1:PgTyped_Pg_Client_t, _2:booksByAuthorParams) => Promise +} = BookService__sqlJS.BooksByAuthor as any; diff --git a/packages/example/src/books/BookService__sql.res b/packages/example/src/books/BookService__sql.res index 6210a3ca..20042982 100644 --- a/packages/example/src/books/BookService__sql.res +++ b/packages/example/src/books/BookService__sql.res @@ -14,19 +14,19 @@ type categoryArray = array /** 'FindBookById' parameters type */ @gentype type findBookByIdParams = { - id?: Null.t, + id?: int, } /** 'FindBookById' return type */ @gentype type findBookByIdResult = { - author_id: Null.t, - big_int: Null.t, - categories: Null.t, + author_id: option, + big_int: option, + categories: option, id: int, - meta: Null.t, - name: Null.t, - rank: Null.t, + meta: option, + name: option, + rank: option, } /** 'FindBookById' query type */ @@ -104,13 +104,13 @@ type booksByAuthorParams = { /** 'BooksByAuthor' return type */ @gentype type booksByAuthorResult = { - author_id: Null.t, - big_int: Null.t, - categories: Null.t, + author_id: option, + big_int: option, + categories: option, id: int, - meta: Null.t, - name: Null.t, - rank: Null.t, + meta: option, + name: option, + rank: option, } /** 'BooksByAuthor' query type */ diff --git a/packages/example/src/books/books.sql b/packages/example/src/books/books.sql index 1b64a43d..b4e64eef 100644 --- a/packages/example/src/books/books.sql +++ b/packages/example/src/books/books.sql @@ -19,6 +19,12 @@ SELECT * FROM books WHERE name = 'שקל'; INSERT INTO books (rank, name, author_id, categories) VALUES :books RETURNING id as book_id; +/* + @name InsertBook +*/ +INSERT INTO books (rank, name, author_id, categories) +VALUES (:rank!, :name!, :author_id!, :categories) RETURNING id as book_id; + /* @name UpdateBooksCustom */ diff --git a/packages/example/src/books/books__sql.gen.tsx b/packages/example/src/books/books__sql.gen.tsx index cd8c75cf..e3416f09 100644 --- a/packages/example/src/books/books__sql.gen.tsx +++ b/packages/example/src/books/books__sql.gen.tsx @@ -9,8 +9,6 @@ import type {Pg_Client_t as PgTyped_Pg_Client_t} from 'pgtyped-rescript/src/res/ import type {t as JSON_t} from './JSON.gen'; -import type {t as Null_t} from './Null.gen'; - export type category = "novel" | "science-fiction" | "thriller"; export type iso31661Alpha2 = @@ -246,44 +244,44 @@ export type intArray = number[]; export type stringArray = string[]; /** 'FindBookById' parameters type */ -export type findBookByIdParams = { readonly id?: Null_t }; +export type findBookByIdParams = { readonly id?: number }; /** 'FindBookById' return type */ export type findBookByIdResult = { - readonly author_id: Null_t; - readonly big_int: Null_t; - readonly categories: Null_t; + readonly author_id: (undefined | number); + readonly big_int: (undefined | bigint); + readonly categories: (undefined | categoryArray); readonly id: number; - readonly meta: Null_t; - readonly name: Null_t; - readonly rank: Null_t + readonly meta: (undefined | arrayJSON_t); + readonly name: (undefined | string); + readonly rank: (undefined | number) }; /** 'FindBookById' query type */ export type findBookByIdQuery = { readonly params: findBookByIdParams; readonly result: findBookByIdResult }; /** 'FindBookByCategory' parameters type */ -export type findBookByCategoryParams = { readonly category?: Null_t }; +export type findBookByCategoryParams = { readonly category?: category }; /** 'FindBookByCategory' return type */ export type findBookByCategoryResult = { - readonly author_id: Null_t; - readonly big_int: Null_t; - readonly categories: Null_t; + readonly author_id: (undefined | number); + readonly big_int: (undefined | bigint); + readonly categories: (undefined | categoryArray); readonly id: number; - readonly meta: Null_t; - readonly name: Null_t; - readonly rank: Null_t + readonly meta: (undefined | arrayJSON_t); + readonly name: (undefined | string); + readonly rank: (undefined | number) }; /** 'FindBookByCategory' query type */ export type findBookByCategoryQuery = { readonly params: findBookByCategoryParams; readonly result: findBookByCategoryResult }; /** 'FindBookNameOrRank' parameters type */ -export type findBookNameOrRankParams = { readonly name?: Null_t; readonly rank?: Null_t }; +export type findBookNameOrRankParams = { readonly name?: string; readonly rank?: number }; /** 'FindBookNameOrRank' return type */ -export type findBookNameOrRankResult = { readonly id: number; readonly name: Null_t }; +export type findBookNameOrRankResult = { readonly id: number; readonly name: (undefined | string) }; /** 'FindBookNameOrRank' query type */ export type findBookNameOrRankQuery = { readonly params: findBookNameOrRankParams; readonly result: findBookNameOrRankResult }; @@ -293,13 +291,13 @@ export type findBookUnicodeParams = void; /** 'FindBookUnicode' return type */ export type findBookUnicodeResult = { - readonly author_id: Null_t; - readonly big_int: Null_t; - readonly categories: Null_t; + readonly author_id: (undefined | number); + readonly big_int: (undefined | bigint); + readonly categories: (undefined | categoryArray); readonly id: number; - readonly meta: Null_t; - readonly name: Null_t; - readonly rank: Null_t + readonly meta: (undefined | arrayJSON_t); + readonly name: (undefined | string); + readonly rank: (undefined | number) }; /** 'FindBookUnicode' query type */ @@ -321,8 +319,22 @@ export type insertBooksResult = { readonly book_id: number }; /** 'InsertBooks' query type */ export type insertBooksQuery = { readonly params: insertBooksParams; readonly result: insertBooksResult }; +/** 'InsertBook' parameters type */ +export type insertBookParams = { + readonly author_id: number; + readonly categories?: categoryArray; + readonly name: string; + readonly rank: number +}; + +/** 'InsertBook' return type */ +export type insertBookResult = { readonly book_id: number }; + +/** 'InsertBook' query type */ +export type insertBookQuery = { readonly params: insertBookParams; readonly result: insertBookResult }; + /** 'UpdateBooksCustom' parameters type */ -export type updateBooksCustomParams = { readonly id: number; readonly rank?: Null_t }; +export type updateBooksCustomParams = { readonly id: number; readonly rank?: number }; /** 'UpdateBooksCustom' return type */ export type updateBooksCustomResult = void; @@ -333,8 +345,8 @@ export type updateBooksCustomQuery = { readonly params: updateBooksCustomParams; /** 'UpdateBooks' parameters type */ export type updateBooksParams = { readonly id: number; - readonly name?: Null_t; - readonly rank?: Null_t + readonly name?: string; + readonly rank?: number }; /** 'UpdateBooks' return type */ @@ -346,7 +358,7 @@ export type updateBooksQuery = { readonly params: updateBooksParams; readonly re /** 'UpdateBooksRankNotNull' parameters type */ export type updateBooksRankNotNullParams = { readonly id: number; - readonly name?: Null_t; + readonly name?: string; readonly rank: number }; @@ -361,23 +373,23 @@ export type getBooksByAuthorNameParams = { readonly authorName: string }; /** 'GetBooksByAuthorName' return type */ export type getBooksByAuthorNameResult = { - readonly author_id: Null_t; - readonly big_int: Null_t; - readonly categories: Null_t; + readonly author_id: (undefined | number); + readonly big_int: (undefined | bigint); + readonly categories: (undefined | categoryArray); readonly id: number; - readonly meta: Null_t; - readonly name: Null_t; - readonly rank: Null_t + readonly meta: (undefined | arrayJSON_t); + readonly name: (undefined | string); + readonly rank: (undefined | number) }; /** 'GetBooksByAuthorName' query type */ export type getBooksByAuthorNameQuery = { readonly params: getBooksByAuthorNameParams; readonly result: getBooksByAuthorNameResult }; /** 'AggregateEmailsAndTest' parameters type */ -export type aggregateEmailsAndTestParams = { readonly testAges?: Null_t }; +export type aggregateEmailsAndTestParams = { readonly testAges?: intArray }; /** 'AggregateEmailsAndTest' return type */ -export type aggregateEmailsAndTestResult = { readonly agetest: Null_t; readonly emails: stringArray }; +export type aggregateEmailsAndTestResult = { readonly agetest: (undefined | boolean); readonly emails: stringArray }; /** 'AggregateEmailsAndTest' query type */ export type aggregateEmailsAndTestQuery = { readonly params: aggregateEmailsAndTestParams; readonly result: aggregateEmailsAndTestResult }; @@ -395,7 +407,7 @@ export type getBooksQuery = { readonly params: getBooksParams; readonly result: export type countBooksParams = void; /** 'CountBooks' return type */ -export type countBooksResult = { readonly book_count: Null_t }; +export type countBooksResult = { readonly book_count: (undefined | bigint) }; /** 'CountBooks' query type */ export type countBooksQuery = { readonly params: countBooksParams; readonly result: countBooksResult }; @@ -489,6 +501,22 @@ export const InsertBooks_execute: (_1:PgTyped_Pg_Client_t, _2:insertBooksParams) export const insertBooks: (params:insertBooksParams, client:PgTyped_Pg_Client_t) => Promise = books__sqlJS.insertBooks as any; +/** Returns an array of all matched results. */ +export const InsertBook_many: (_1:PgTyped_Pg_Client_t, _2:insertBookParams) => Promise = books__sqlJS.InsertBook.many as any; + +/** Returns exactly 1 result. Returns `None` if more or less than exactly 1 result is returned. */ +export const InsertBook_one: (_1:PgTyped_Pg_Client_t, _2:insertBookParams) => Promise<(undefined | insertBookResult)> = books__sqlJS.InsertBook.one as any; + +/** Returns exactly 1 result. Returns `Error` (with an optionally provided `errorMessage`) if more or less than exactly 1 result is returned. */ +export const InsertBook_expectOne: (_1:PgTyped_Pg_Client_t, _2:insertBookParams, errorMessage:(undefined | string)) => Promise< + { TAG: "Ok"; _0: insertBookResult } + | { TAG: "Error"; _0: string }> = books__sqlJS.InsertBook.expectOne as any; + +/** Executes the query, but ignores whatever is returned by it. */ +export const InsertBook_execute: (_1:PgTyped_Pg_Client_t, _2:insertBookParams) => Promise = books__sqlJS.InsertBook.execute as any; + +export const insertBook: (params:insertBookParams, client:PgTyped_Pg_Client_t) => Promise = books__sqlJS.insertBook as any; + /** Returns an array of all matched results. */ export const UpdateBooksCustom_many: (_1:PgTyped_Pg_Client_t, _2:updateBooksCustomParams) => Promise = books__sqlJS.UpdateBooksCustom.many as any; @@ -655,6 +683,25 @@ export const UpdateBooks: { execute: (_1:PgTyped_Pg_Client_t, _2:updateBooksParams) => Promise } = books__sqlJS.UpdateBooks as any; +export const InsertBook: { + /** Returns exactly 1 result. Returns `Error` (with an optionally provided `errorMessage`) if more or less than exactly 1 result is returned. */ + expectOne: (_1:PgTyped_Pg_Client_t, _2:insertBookParams, errorMessage:(undefined | string)) => Promise< + { + TAG: "Ok"; + _0: insertBookResult + } + | { + TAG: "Error"; + _0: string + }>; + /** Returns exactly 1 result. Returns `None` if more or less than exactly 1 result is returned. */ + one: (_1:PgTyped_Pg_Client_t, _2:insertBookParams) => Promise<(undefined | insertBookResult)>; + /** Returns an array of all matched results. */ + many: (_1:PgTyped_Pg_Client_t, _2:insertBookParams) => Promise; + /** Executes the query, but ignores whatever is returned by it. */ + execute: (_1:PgTyped_Pg_Client_t, _2:insertBookParams) => Promise +} = books__sqlJS.InsertBook as any; + export const FindBookByCategory: { /** Returns exactly 1 result. Returns `Error` (with an optionally provided `errorMessage`) if more or less than exactly 1 result is returned. */ expectOne: (_1:PgTyped_Pg_Client_t, _2:findBookByCategoryParams, errorMessage:(undefined | string)) => Promise< diff --git a/packages/example/src/books/books__sql.res b/packages/example/src/books/books__sql.res index af3aa0cf..75446214 100644 --- a/packages/example/src/books/books__sql.res +++ b/packages/example/src/books/books__sql.res @@ -23,19 +23,19 @@ type stringArray = array /** 'FindBookById' parameters type */ @gentype type findBookByIdParams = { - id?: Null.t, + id?: int, } /** 'FindBookById' return type */ @gentype type findBookByIdResult = { - author_id: Null.t, - big_int: Null.t, - categories: Null.t, + author_id: option, + big_int: option, + categories: option, id: int, - meta: Null.t, - name: Null.t, - rank: Null.t, + meta: option, + name: option, + rank: option, } /** 'FindBookById' query type */ @@ -107,19 +107,19 @@ let findBookById = (params, ~client) => FindBookById.many(client, params) /** 'FindBookByCategory' parameters type */ @gentype type findBookByCategoryParams = { - category?: Null.t, + category?: category, } /** 'FindBookByCategory' return type */ @gentype type findBookByCategoryResult = { - author_id: Null.t, - big_int: Null.t, - categories: Null.t, + author_id: option, + big_int: option, + categories: option, id: int, - meta: Null.t, - name: Null.t, - rank: Null.t, + meta: option, + name: option, + rank: option, } /** 'FindBookByCategory' query type */ @@ -191,15 +191,15 @@ let findBookByCategory = (params, ~client) => FindBookByCategory.many(client, pa /** 'FindBookNameOrRank' parameters type */ @gentype type findBookNameOrRankParams = { - name?: Null.t, - rank?: Null.t, + name?: string, + rank?: int, } /** 'FindBookNameOrRank' return type */ @gentype type findBookNameOrRankResult = { id: int, - name: Null.t, + name: option, } /** 'FindBookNameOrRank' query type */ @@ -277,13 +277,13 @@ type findBookUnicodeParams = unit /** 'FindBookUnicode' return type */ @gentype type findBookUnicodeResult = { - author_id: Null.t, - big_int: Null.t, - categories: Null.t, + author_id: option, + big_int: option, + categories: option, id: int, - meta: Null.t, - name: Null.t, - rank: Null.t, + meta: option, + name: option, + rank: option, } /** 'FindBookUnicode' query type */ @@ -438,11 +438,93 @@ module InsertBooks: { let insertBooks = (params, ~client) => InsertBooks.many(client, params) +/** 'InsertBook' parameters type */ +@gentype +type insertBookParams = { + author_id: int, + categories?: categoryArray, + name: string, + rank: int, +} + +/** 'InsertBook' return type */ +@gentype +type insertBookResult = { + book_id: int, +} + +/** 'InsertBook' query type */ +@gentype +type insertBookQuery = { + params: insertBookParams, + result: insertBookResult, +} + +%%private(let insertBookIR: IR.t = %raw(`{"usedParamSet":{"rank":true,"name":true,"author_id":true,"categories":true},"params":[{"name":"rank","required":true,"transform":{"type":"scalar"},"locs":[{"a":62,"b":67}]},{"name":"name","required":true,"transform":{"type":"scalar"},"locs":[{"a":70,"b":75}]},{"name":"author_id","required":true,"transform":{"type":"scalar"},"locs":[{"a":78,"b":88}]},{"name":"categories","required":false,"transform":{"type":"scalar"},"locs":[{"a":91,"b":101}]}],"statement":"INSERT INTO books (rank, name, author_id, categories)\nVALUES (:rank!, :name!, :author_id!, :categories) RETURNING id as book_id"}`)) + +/** + * Query generated from SQL: + * ``` + * INSERT INTO books (rank, name, author_id, categories) + * VALUES (:rank!, :name!, :author_id!, :categories) RETURNING id as book_id + * ``` + */ +@gentype +module InsertBook: { + /** Returns an array of all matched results. */ + @gentype + let many: (PgTyped.Pg.Client.t, insertBookParams) => promise> + /** Returns exactly 1 result. Returns `None` if more or less than exactly 1 result is returned. */ + @gentype + let one: (PgTyped.Pg.Client.t, insertBookParams) => promise> + + /** Returns exactly 1 result. Returns `Error` (with an optionally provided `errorMessage`) if more or less than exactly 1 result is returned. */ + @gentype + let expectOne: ( + PgTyped.Pg.Client.t, + insertBookParams, + ~errorMessage: string=? + ) => promise> + + /** Executes the query, but ignores whatever is returned by it. */ + @gentype + let execute: (PgTyped.Pg.Client.t, insertBookParams) => promise +} = { + @module("@pgtyped/runtime") @new external insertBook: IR.t => PreparedStatement.t = "PreparedQuery"; + let query = insertBook(insertBookIR) + let query = (params, ~client) => query->PreparedStatement.run(params, ~client) + + @gentype + let many = (client, params) => query(params, ~client) + + @gentype + let one = async (client, params) => switch await query(params, ~client) { + | [item] => Some(item) + | _ => None + } + + @gentype + let expectOne = async (client, params, ~errorMessage=?) => switch await query(params, ~client) { + | [item] => Ok(item) + | _ => Error(errorMessage->Option.getOr("More or less than one item was returned")) + } + + @gentype + let execute = async (client, params) => { + let _ = await query(params, ~client) + } +} + +@gentype +@deprecated("Use 'InsertBook.many' directly instead") +let insertBook = (params, ~client) => InsertBook.many(client, params) + + /** 'UpdateBooksCustom' parameters type */ @gentype type updateBooksCustomParams = { id: int, - rank?: Null.t, + rank?: int, } /** 'UpdateBooksCustom' return type */ @@ -527,8 +609,8 @@ let updateBooksCustom = (params, ~client) => UpdateBooksCustom.many(client, para @gentype type updateBooksParams = { id: int, - name?: Null.t, - rank?: Null.t, + name?: string, + rank?: int, } /** 'UpdateBooks' return type */ @@ -610,7 +692,7 @@ let updateBooks = (params, ~client) => UpdateBooks.many(client, params) @gentype type updateBooksRankNotNullParams = { id: int, - name?: Null.t, + name?: string, rank: int, } @@ -697,13 +779,13 @@ type getBooksByAuthorNameParams = { /** 'GetBooksByAuthorName' return type */ @gentype type getBooksByAuthorNameResult = { - author_id: Null.t, - big_int: Null.t, - categories: Null.t, + author_id: option, + big_int: option, + categories: option, id: int, - meta: Null.t, - name: Null.t, - rank: Null.t, + meta: option, + name: option, + rank: option, } /** 'GetBooksByAuthorName' query type */ @@ -777,13 +859,13 @@ let getBooksByAuthorName = (params, ~client) => GetBooksByAuthorName.many(client /** 'AggregateEmailsAndTest' parameters type */ @gentype type aggregateEmailsAndTestParams = { - testAges?: Null.t, + testAges?: intArray, } /** 'AggregateEmailsAndTest' return type */ @gentype type aggregateEmailsAndTestResult = { - agetest: Null.t, + agetest: option, emails: stringArray, } @@ -937,7 +1019,7 @@ type countBooksParams = unit /** 'CountBooks' return type */ @gentype type countBooksResult = { - book_count: Null.t, + book_count: option, } /** 'CountBooks' query type */ diff --git a/packages/example/src/comments/comments__sql.gen.tsx b/packages/example/src/comments/comments__sql.gen.tsx index f9463a12..0b20c64e 100644 --- a/packages/example/src/comments/comments__sql.gen.tsx +++ b/packages/example/src/comments/comments__sql.gen.tsx @@ -7,17 +7,15 @@ const comments__sqlJS = require('./comments__sql.js'); import type {Pg_Client_t as PgTyped_Pg_Client_t} from 'pgtyped-rescript/src/res/PgTyped.gen'; -import type {t as Null_t} from './Null.gen'; - /** 'GetAllComments' parameters type */ export type getAllCommentsParams = { readonly id: number }; /** 'GetAllComments' return type */ export type getAllCommentsResult = { - readonly body: Null_t; - readonly book_id: Null_t; + readonly body: (undefined | string); + readonly book_id: (undefined | number); readonly id: number; - readonly user_id: Null_t + readonly user_id: (undefined | number) }; /** 'GetAllComments' query type */ @@ -28,10 +26,10 @@ export type getAllCommentsByIdsParams = { readonly ids: number[] }; /** 'GetAllCommentsByIds' return type */ export type getAllCommentsByIdsResult = { - readonly body: Null_t; - readonly book_id: Null_t; + readonly body: (undefined | string); + readonly book_id: (undefined | number); readonly id: number; - readonly user_id: Null_t + readonly user_id: (undefined | number) }; /** 'GetAllCommentsByIds' query type */ @@ -44,10 +42,10 @@ export type insertCommentParams = { readonly comments: insertCommentParams_comme /** 'InsertComment' return type */ export type insertCommentResult = { - readonly body: Null_t; - readonly book_id: Null_t; + readonly body: (undefined | string); + readonly book_id: (undefined | number); readonly id: number; - readonly user_id: Null_t + readonly user_id: (undefined | number) }; /** 'InsertComment' query type */ @@ -57,7 +55,7 @@ export type insertCommentQuery = { readonly params: insertCommentParams; readonl export type selectExistsTestParams = void; /** 'SelectExistsTest' return type */ -export type selectExistsTestResult = { readonly isTransactionExists: Null_t }; +export type selectExistsTestResult = { readonly isTransactionExists: (undefined | boolean) }; /** 'SelectExistsTest' query type */ export type selectExistsTestQuery = { readonly params: selectExistsTestParams; readonly result: selectExistsTestResult }; diff --git a/packages/example/src/comments/comments__sql.res b/packages/example/src/comments/comments__sql.res index 79dab270..b7d8cf22 100644 --- a/packages/example/src/comments/comments__sql.res +++ b/packages/example/src/comments/comments__sql.res @@ -11,10 +11,10 @@ type getAllCommentsParams = { /** 'GetAllComments' return type */ @gentype type getAllCommentsResult = { - body: Null.t, - book_id: Null.t, + body: option, + book_id: option, id: int, - user_id: Null.t, + user_id: option, } /** 'GetAllComments' query type */ @@ -92,10 +92,10 @@ type getAllCommentsByIdsParams = { /** 'GetAllCommentsByIds' return type */ @gentype type getAllCommentsByIdsResult = { - body: Null.t, - book_id: Null.t, + body: option, + book_id: option, id: int, - user_id: Null.t, + user_id: option, } /** 'GetAllCommentsByIds' query type */ @@ -178,10 +178,10 @@ type insertCommentParams = { /** 'InsertComment' return type */ @gentype type insertCommentResult = { - body: Null.t, - book_id: Null.t, + body: option, + book_id: option, id: int, - user_id: Null.t, + user_id: option, } /** 'InsertComment' query type */ @@ -259,7 +259,7 @@ type selectExistsTestParams = unit /** 'SelectExistsTest' return type */ @gentype type selectExistsTestResult = { - isTransactionExists: Null.t, + isTransactionExists: option, } /** 'SelectExistsTest' query type */ diff --git a/packages/example/src/notifications/notifications__sql.gen.tsx b/packages/example/src/notifications/notifications__sql.gen.tsx index 8a66d8bb..c01e8ce5 100644 --- a/packages/example/src/notifications/notifications__sql.gen.tsx +++ b/packages/example/src/notifications/notifications__sql.gen.tsx @@ -11,8 +11,6 @@ import type {dateOrString as PgTyped_dateOrString} from 'pgtyped-rescript/src/re import type {t as JSON_t} from './JSON.gen'; -import type {t as Null_t} from './Null.gen'; - export type notification_type = "deadline" | "notification" | "reminder"; export type sendNotificationsParams_notifications = { @@ -31,7 +29,7 @@ export type sendNotificationsResult = { readonly notification_id: number }; export type sendNotificationsQuery = { readonly params: sendNotificationsParams; readonly result: sendNotificationsResult }; /** 'GetNotifications' parameters type */ -export type getNotificationsParams = { readonly date: PgTyped_dateOrString; readonly userId?: Null_t }; +export type getNotificationsParams = { readonly date: PgTyped_dateOrString; readonly userId?: number }; /** 'GetNotifications' return type */ export type getNotificationsResult = { @@ -39,7 +37,7 @@ export type getNotificationsResult = { readonly id: number; readonly payload: JSON_t; readonly type: notification_type; - readonly user_id: Null_t + readonly user_id: (undefined | number) }; /** 'GetNotifications' query type */ diff --git a/packages/example/src/notifications/notifications__sql.res b/packages/example/src/notifications/notifications__sql.res index 3a431c66..dcb9e671 100644 --- a/packages/example/src/notifications/notifications__sql.res +++ b/packages/example/src/notifications/notifications__sql.res @@ -94,7 +94,7 @@ let sendNotifications = (params, ~client) => SendNotifications.many(client, para @gentype type getNotificationsParams = { date: dateOrString, - userId?: Null.t, + userId?: int, } /** 'GetNotifications' return type */ @@ -104,7 +104,7 @@ type getNotificationsResult = { id: int, payload: JSON.t, @as("type") type_: notification_type, - user_id: Null.t, + user_id: option, } /** 'GetNotifications' query type */ diff --git a/packages/example/src/rescript.test.res b/packages/example/src/rescript.test.res index 4dabdcf2..0ab835ac 100644 --- a/packages/example/src/rescript.test.res +++ b/packages/example/src/rescript.test.res @@ -93,7 +93,7 @@ testAsync("select query with parameters", async () => { testAsync("select query with dynamic or", async () => { let result = await getClient()->Books.FindBookNameOrRank.many({ - rank: Value(1), + rank: 1, }) expect(result)->Expect.toMatchSnapshot }) @@ -113,17 +113,53 @@ testAsync("insert query with parameter spread", async () => { | _ => panic("Unexpected result inserting books") } - switch await getClient()->Books.FindBookById.many({id: Value(insertedBookId)}) { + switch await getClient()->Books.FindBookById.many({id: insertedBookId}) { | [insertedBook] => expect(insertedBook.categories)->Expect.toEqual("{novel,science-fiction}") | _ => panic("Unexpected result fetching newly inserted book") } }) +testAsync("insert query with non-supplied optional value", async () => { + let insertedBookId = switch await getClient()->Books.InsertBooks.many({ + books: [ + { + authorId: 1, + name: "A Brief History of Time: From the Big Bang to Black Holes", + rank: 1, + }, + ], + }) { + | [{book_id: id}] => id + | _ => panic("Unexpected result inserting books") + } + + switch await getClient()->Books.FindBookById.many({id: insertedBookId}) { + | [insertedBook] => expect(insertedBook.categories)->Expect.toEqual(None) + | _ => panic("Unexpected result fetching newly inserted book") + } +}) + +testAsync("insert query with non-supplied opt value", async () => { + let insertedBookId = switch await getClient()->Books.InsertBook.one({ + author_id: 1, + name: "A Brief History of Time: From the Big Bang to Black Holes", + rank: 1, + }) { + | Some({book_id: id}) => id + | None => panic("Unexpected result inserting book") + } + + switch await getClient()->Books.FindBookById.one({id: insertedBookId}) { + | Some(insertedBook) => expect(insertedBook.categories)->Expect.toEqual(None) + | None => panic("Unexpected result fetching newly inserted book") + } +}) + testAsync("update query with a non-null parameter override", async () => { let _ = await getClient()->Books.UpdateBooks.many({ id: 2, - rank: Value(12), - name: Value("Another title"), + rank: 12, + name: "Another title", }) }) @@ -140,14 +176,14 @@ testAsync("insert query with an inline sql comment", async () => { }) testAsync("dynamic update query", async () => { - let _ = await getClient()->Books.UpdateBooksCustom.many({id: 2, rank: Value(13)}) + let _ = await getClient()->Books.UpdateBooksCustom.many({id: 2, rank: 13}) }) testAsync("update query with a multiple non-null parameter overrides", async () => { let _ = await getClient()->Books.UpdateBooksRankNotNull.many({ id: 2, rank: 12, - name: Value("Another title"), + name: "Another title", }) }) @@ -159,7 +195,7 @@ testAsync("select query with join and a parameter override", async () => { }) testAsync("select query with aggregation", async () => { - switch await getClient()->Books.AggregateEmailsAndTest.many({testAges: Value([35, 23, 19])}) { + switch await getClient()->Books.AggregateEmailsAndTest.many({testAges: [35, 23, 19]}) { | [aggregateData] => expect(aggregateData.agetest)->Expect.toBe(true) expect(aggregateData.emails)->Expect.toEqual([ diff --git a/packages/runtime/src/tag.ts b/packages/runtime/src/tag.ts index 26619574..d5c39edb 100644 --- a/packages/runtime/src/tag.ts +++ b/packages/runtime/src/tag.ts @@ -23,9 +23,12 @@ function mapQueryResultRows(rows: any[]): any[] { for (const columnName in row) { if (isHintedColumn(columnName)) { const newColumnNameWithoutSuffix = columnName.slice(0, -1); - row[newColumnNameWithoutSuffix] = row[columnName]; + row[newColumnNameWithoutSuffix] = row[columnName] ?? undefined; delete row[columnName]; } + if (row[columnName] === null) { + row[columnName] = undefined; + } } } return rows;