diff --git a/packages/backend/src/execution/composeExecutionResults.ts b/packages/backend/src/execution/composeExecutionResults.ts index b0ea7505..7e8bcbd4 100644 --- a/packages/backend/src/execution/composeExecutionResults.ts +++ b/packages/backend/src/execution/composeExecutionResults.ts @@ -1,4 +1,10 @@ -import { AnyDB, AnyTable, QueryResult } from '@synthql/queries'; +import { + AnyDB, + AnyQuery, + AnyTable, + DeferredResult, + QueryResult, +} from '@synthql/queries'; import { applyCardinality } from '../query/applyCardinality'; import { assertHasKey } from '../util/asserts/assertHasKey'; import { setIn } from '../util/tree/setIn'; @@ -13,9 +19,9 @@ export function composeExecutionResults( composeExecutionResultsRecursively(node, queryResult); } - return applyCardinality( + return applyCardinalityAndDeferredResult( queryResult, - tree.root.inputQuery.cardinality ?? 'many', + tree.root.inputQuery, ) as QueryResult; } @@ -40,13 +46,30 @@ function composeExecutionResultsRecursively( return true; }; const rows = result.filter((row) => predicate(row)); - return applyCardinality(rows, inputQuery.cardinality ?? 'many', { - query: inputQuery, - row: rows, - }); + + return applyCardinalityAndDeferredResult(rows, inputQuery); }); for (const child of node.children) { composeExecutionResultsRecursively(child, queryResult); } } + +function applyDeferredQueryResult( + result: T, + defer: boolean = false, +): DeferredResult | T { + if (!defer) { + return result; + } + return { status: 'done', data: result }; +} + +function applyCardinalityAndDeferredResult(rows: T[], inputQuery: AnyQuery) { + const withCardinality = applyCardinality(rows, inputQuery.cardinality ?? 'many', { + query: inputQuery, + row: rows, + }); + + return applyDeferredQueryResult(withCardinality, inputQuery.lazy); +} diff --git a/packages/backend/src/execution/execute.ts b/packages/backend/src/execution/execute.ts index c5b95da2..99bcee3d 100644 --- a/packages/backend/src/execution/execute.ts +++ b/packages/backend/src/execution/execute.ts @@ -3,6 +3,7 @@ import { composeExecutionResults } from './composeExecutionResults'; import { createExecutionPlan } from './planning/createExecutionPlan'; import { executePlan } from './execution/executePlan'; import { QueryExecutor } from './types'; +import { shouldYield } from './execution/shouldYield'; export interface ExecuteProps { executors: Array; @@ -40,6 +41,8 @@ export async function* execute>( const plan = createExecutionPlan(query, props); for await (const resultTree of executePlan(plan, props)) { - yield composeExecutionResults(resultTree); + if (shouldYield(resultTree)) { + yield composeExecutionResults(resultTree); + } } } diff --git a/packages/backend/src/execution/execution/executePlan.ts b/packages/backend/src/execution/execution/executePlan.ts index 33aa4ea2..a5621912 100644 --- a/packages/backend/src/execution/execution/executePlan.ts +++ b/packages/backend/src/execution/execution/executePlan.ts @@ -52,6 +52,7 @@ export function executePlan( filters: createFilters(planNode), inputQuery: planNode.inputQuery, result: rows, + planNode, children: [], }; }, diff --git a/packages/backend/src/execution/execution/shouldYield.ts b/packages/backend/src/execution/execution/shouldYield.ts new file mode 100644 index 00000000..43dea3ea --- /dev/null +++ b/packages/backend/src/execution/execution/shouldYield.ts @@ -0,0 +1,25 @@ +import { ExecResultNode, ExecResultTree } from '../types'; + +export function shouldYield(tree: ExecResultTree): boolean { + return shouldYieldNode(tree.root); +} + +function shouldYieldNode(node: ExecResultNode): boolean { + const plannedChildren = node.planNode.children; + const executedChildren = node.children; + + const allChildrenExecuted = + plannedChildren.length === executedChildren.length; + + if (allChildrenExecuted) { + return true; + } + + for (const child of executedChildren) { + if (!shouldYieldNode(child)) { + return false; + } + } + + return false; +} diff --git a/packages/backend/src/execution/executors/PgExecutor/PgExecutor.ts b/packages/backend/src/execution/executors/PgExecutor/PgExecutor.ts index fb1fb372..3da09667 100644 --- a/packages/backend/src/execution/executors/PgExecutor/PgExecutor.ts +++ b/packages/backend/src/execution/executors/PgExecutor/PgExecutor.ts @@ -131,7 +131,10 @@ export class PgExecutor implements QueryExecutor { try { if (this.props.logging) { - console.log(format(sql, { language: 'postgresql' })); + console.log( + `[${query.name}]:`, + format(sql, { language: 'postgresql' }), + ); } const queryResult = await client.query(sql, params); diff --git a/packages/backend/src/execution/types.d.ts b/packages/backend/src/execution/types.d.ts index 3df2b411..464139e0 100644 --- a/packages/backend/src/execution/types.d.ts +++ b/packages/backend/src/execution/types.d.ts @@ -158,6 +158,7 @@ export interface ExecResultNode { * The original query that was executed. */ inputQuery: AnyQuery; + planNode: ExecutionPlanNode; children: ExecResultNode[]; } diff --git a/packages/backend/src/query/iterateQuery.test.ts b/packages/backend/src/query/iterateQuery.test.ts index 52671534..91e83d53 100644 --- a/packages/backend/src/query/iterateQuery.test.ts +++ b/packages/backend/src/query/iterateQuery.test.ts @@ -194,6 +194,7 @@ describe('iterateQuery', () => { where: { film_id: 1, }, + cardinality: 'many', }, expected: [ { @@ -201,6 +202,7 @@ describe('iterateQuery', () => { from: 'film', select: {}, where: { film_id: 1 }, + cardinality: 'many', }, insertionPath: [], }, diff --git a/packages/backend/src/tests/e2e/nxm.test.ts b/packages/backend/src/tests/e2e/nxm.test.ts index 8992cf79..c5123602 100644 --- a/packages/backend/src/tests/e2e/nxm.test.ts +++ b/packages/backend/src/tests/e2e/nxm.test.ts @@ -41,7 +41,6 @@ describe('n x m', () => { executors: [ new PgExecutor({ defaultSchema: 'public', - logging: true, pool, }), ], diff --git a/packages/backend/src/tests/e2e/payments.test.ts b/packages/backend/src/tests/e2e/payments.test.ts index f9412f9e..a407aa18 100644 --- a/packages/backend/src/tests/e2e/payments.test.ts +++ b/packages/backend/src/tests/e2e/payments.test.ts @@ -17,7 +17,6 @@ describe('e2e', () => { test.skip(describeQuery(q), async () => { const pgExecutor = new PgExecutor({ defaultSchema: 'public', - logging: true, pool, }); diff --git a/packages/backend/src/tests/e2e/select.test.ts b/packages/backend/src/tests/e2e/select.test.ts index d8cdee95..6a4b0cfa 100644 --- a/packages/backend/src/tests/e2e/select.test.ts +++ b/packages/backend/src/tests/e2e/select.test.ts @@ -164,4 +164,43 @@ describe('select', () => { Array.from(expected).sort(comparator), ); }); + + test('defer', async () => { + const limit = 20; + const language = from('language') + .columns('name') + .where({ language_id: col('film.language_id') }) + .defer() + .first(); + + const query = from('film') + .columns('title') + .include({ language }) + .limit(limit) + .all(); + + const languageNonDeferred = from('language') + .columns('name') + .where({ language_id: col('film.language_id') }) + .first(); + + const queryNonDeferred = from('film') + .columns('title') + .include({ language: languageNonDeferred }) + .limit(limit) + .all(); + + const result = await run(query); + const resultNonDeferred = await run(queryNonDeferred); + + expect( + result.map( + (r) => + r.language.status === 'done' && { + ...r, + language: r.language.data, + }, + ), + ).toEqual(resultNonDeferred); + }); }); diff --git a/packages/backend/src/tests/e2e/store-with-customers.test.ts b/packages/backend/src/tests/e2e/store-with-customers.test.ts index 4e43d50a..14f5c908 100644 --- a/packages/backend/src/tests/e2e/store-with-customers.test.ts +++ b/packages/backend/src/tests/e2e/store-with-customers.test.ts @@ -36,7 +36,6 @@ describe('e2e', () => { const pgExecutor = new PgExecutor({ defaultSchema: 'public', - logging: true, pool, }); const execProps = { diff --git a/packages/backend/src/tests/e2e/store-with-films.test.ts b/packages/backend/src/tests/e2e/store-with-films.test.ts index b89f9aaf..0d16e00c 100644 --- a/packages/backend/src/tests/e2e/store-with-films.test.ts +++ b/packages/backend/src/tests/e2e/store-with-films.test.ts @@ -40,7 +40,6 @@ describe('e2e', () => { const pgExecutor = new PgExecutor({ defaultSchema: 'public', - logging: true, pool, }); const execProps = { diff --git a/packages/docs/static/reference/assets/navigation.js b/packages/docs/static/reference/assets/navigation.js index abb0d21d..c7721b38 100644 --- a/packages/docs/static/reference/assets/navigation.js +++ b/packages/docs/static/reference/assets/navigation.js @@ -1 +1 @@ -window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAA5WXbW/bIBCA/4s/p+vard2Wb2kaad2mNkuiTVNVVRRfGlQCDuAq1rT/Phk78Qv4oF/DwwMcx+V8/zcxsDfJOHki9AVEeqoVTUZJRswmGSdbmeYc9Gk9+KgVfbcxW56Mkhcm0mR8PkrohvFUgUjG90fZzxxUMRPPTEAjo5xo3ZO1wK747Pzzv9HRtyyE2ez4TCmpcGGbxIythedKZrqxMmFArQkd3qmd0IvDxWVfPudExFlLEtNRyTlQ84No0wjXuaCGSdH1tdCu8fJjR7jNpAa7eNjYsI7yoS3lzJ88lDNP4rRmbohIOagT2GcKtPZbauixhqJTkSogBmbVrDo7vlYu39F9yyAKNCKHcwnYG/xQJRF9olvYm6GDmCLzad0pvZXef/l0dtG+E3fGAnY5tDMweq16ZmjJKszY6dxrOq46NBm9ICaMkjoDOnA9zXj05TyDAEUM+Dbd0x1QdIu7HBSDgTdRD0Zvbip5vhXXsPbWpbbtSGJ16QiVBRGUYeAvo15xMydqiTeYUeGSbmBLgrIKw0Qr8sQhJpQHEJNNRHF91X9ZbYkFQg9oIopeSfeKPKXc67IbD7gsE+vqRGtY58bKMV4xQVRxl2G6AxPrKt+idEqpz1iRIe+UqJQJwplBL6SFBY02w1GZJeI8qyJDb7eh4ny/CM8jhBYLGW8E5XmK2mokZPommcDzpCJCnuDDinpVVbtHFNmCcf+2Hd2RjPIuQOfc+X92pBUWMi5gjYfNAiHLEspuFNNURMgTLEVRdej3BhRqsUCUZcpJrsOuCgtWoJvbyeLP4918tpis7hbLRvtKFCtP1qtDPb7r/9DpqST3dSJtG5Uc+VRgeihn/bY+j6p7STZk9KRaR5SVi4U0FkIku6GPobbEQidnaMemgAz1k3YoulurG9mpFPYHX5/RCLsw2gFVpL2m7+DUNEd5AENZrAe22+Rwo+6y/YB2MrjexVzJV5b688/Z8gFGk28wDj6xgyNq/RZxD/bkViPONdS7wJ0NFyVzbgx3elPMvoGH/wmqzOxOEgAA" \ No newline at end of file +window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE5WXUU/bMBCA/0ueyxhssK1vpVQa2wRdW22aEELGuVIL10ltBzWa9t+nOGnjxM7ZvJLPn8++83G9/5to2OtknDwR+gIiPVWSJqMkJ3qTjJNtlhYc1Gnz8VFJ+m6jtzwZJS9MpMn4fJTQDeOpBJGM74+ynwXIciaemYBWRjlRqiezwK747Pzzv9HRtyyF3uz4TMpM4kKbxIzWxnOZ5aq1MqFBrgkdjtQs6N3DxWVfPudExFkrEtPRjHOg+gdRuhWuC0E1y0TXZ6Fd4+XHjnCbZwrM5mFjyzrKB1vKmb94KGeewrFWbohIOcgT2OcSlPJbGuixgaJLkUogGmb1qqY6vtYu39F92yAK9EYO5xKw1/ihKiL6RLew10MH0WXu07pLeju9//Lp7MLOibtiAbsC7AqM3qtZGdqyvmbsdG6ajrsOLUYTxISWmcqBDqSn/R6dnGcQIIkGX9A93QFFQ9wVIBkMvInmY3Rw04wXW3ENa29fsm1HEutLR6hqiCA1A38b9YrbNVFbvMGMCpd0A1sSlNUYJlqRJw4xV3kAMdlElNdX/ZdlSwwQekATUfZaulfkaeVelwk84DJMrKtzW8M6964c4xUTRJZ3OaY7MLGu6i1mTiv1GWsy5J0SmTJBONNoQiwsaDQVjsoMEedZlTma3ZaK8/0ivIgQGixkvIY1SAnpAlTBnX85trRLhrw3gvIiRaNskJDpW8YEXn81EfIEH2zUa63HSCLJFrQ7Dji6IxnlDSfBwkLGBazxazNAyLKEasrFNDUR8gRbXFR/+70BiVoMEGWZclKosKvGgp3t5nay+PN4N58tJqu7xbLVvhLJqpP1+luP7/o/dGa1jPsmHNtGM478BGFqqGb9tj6PqntFNmT0lFpHlFebhTQGQiS7oR9ZtsRAJ2foJCiBDM2p5lP0FNgMyNNMmD/45pdW2IXRyaomTZq+g9PTHOUBDFWxGgi3reFW3WX7F9qp4CaKucxeWeqvPyfkA4wW3+A9+MQOjqjVW8Q92FNbrbhQ0ESBO1suSuZkDHd6S8y8gYf/O5IR9qYSAAA=" \ No newline at end of file diff --git a/packages/docs/static/reference/assets/search.js b/packages/docs/static/reference/assets/search.js index 7687d576..99eabca9 100644 --- a/packages/docs/static/reference/assets/search.js +++ b/packages/docs/static/reference/assets/search.js @@ -1 +1 @@ -window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file +window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file diff --git a/packages/queries/src/query.test.ts b/packages/queries/src/query.test.ts index 882b9b98..a4a0a936 100644 --- a/packages/queries/src/query.test.ts +++ b/packages/queries/src/query.test.ts @@ -1,5 +1,5 @@ import { describe, test } from 'vitest'; -import { Query, QueryResult, Table, col } from '.'; +import { DeferredResult, Query, QueryResult, Table, col } from '.'; import { DB, from } from './generated'; describe('queries', () => { @@ -144,4 +144,31 @@ describe('queries', () => { }>; }; }); + + test('defer()', () => { + const q = from('customer').columns('email', 'first_name').defer().all(); + + const result = fakeQueryResult(q); + result satisfies DeferredResult< + Array<{ email: string | null; first_name: string }> + >; + }); + + test('defer() ', () => { + const language = from('language') + .columns('name') + .defer() + .where({ + language_id: col('film.language_id'), + }) + .first(); + + const q = from('film').include({ language }).columns('title').all(); + + const result = fakeQueryResult(q); + result satisfies Array<{ + title: string; + language: DeferredResult<{ name: string } | null>; + }>; + }); }); diff --git a/packages/queries/src/query.ts b/packages/queries/src/query.ts index c561cf58..240a07c8 100644 --- a/packages/queries/src/query.ts +++ b/packages/queries/src/query.ts @@ -59,9 +59,11 @@ export class QueryBuilder< lazy: TLazy; groupBy: TGroupBy; hash: string; - name?: string; + name: string; } { - return { + const defaultName = `${this._from}-by-${Object.keys(this._where).join('-and-')}`; + + const query = { from: this._from, where: this._where, select: this._select, @@ -71,33 +73,26 @@ export class QueryBuilder< cardinality: this._cardinality ?? 'many', lazy: this._lazy, groupBy: this._groupBy, - hash: hashQuery({ - from: this._from, - where: this._where, - select: this._select, - include: this._include, - limit: this._limit, - offset: this._offset, - cardinality: this._cardinality ?? 'many', - lazy: this._lazy, - groupBy: this._groupBy, - name: this._name, - }), - name: this._name, + hash: '', + name: this._name ?? defaultName, }; + + query.hash = hashQuery(query); + + return query; } /** * Sets the limit of the query. */ - limit(limit: TLimit) { + limit(limit: number) { return new QueryBuilder< DB, TTable, TWhere, TSelect, TInclude, - TLimit, + number, TOffset, TCardinality, TLazy, @@ -156,30 +151,8 @@ export class QueryBuilder< * for the query, and then builds the query. * Shorthand for `.limit(n).all()`. */ - take(take: TLimit) { - return new QueryBuilder< - DB, - TTable, - TWhere, - TSelect, - TInclude, - TLimit, - TOffset, - 'many', - TLazy, - TGroupBy - >( - this._from, - this._where, - this._select, - this._include, - take, - this._offset, - 'many', - this._lazy, - this._groupBy, - this._name, - ).build(); + take(take: number) { + return this.limit(take).cardinality('many').build(); } /** @@ -215,87 +188,21 @@ export class QueryBuilder< * @alias {@link firstOrThrow} */ one() { - return new QueryBuilder< - DB, - TTable, - TWhere, - TSelect, - TInclude, - 1, - TOffset, - 'one', - TLazy, - TGroupBy - >( - this._from, - this._where, - this._select, - this._include, - 1, - this._offset, - 'one', - this._lazy, - this._groupBy, - this._name, - ).build(); + return this.cardinality('one').limit(1).build(); } /** * @alias {@link all} */ many() { - return new QueryBuilder< - DB, - TTable, - TWhere, - TSelect, - TInclude, - TLimit, - TOffset, - 'many', - TLazy, - TGroupBy - >( - this._from, - this._where, - this._select, - this._include, - this._limit, - this._offset, - 'many', - this._lazy, - this._groupBy, - this._name, - ).build(); + return this.cardinality('many').build(); } /** * @alias {@link first} */ maybe() { - return new QueryBuilder< - DB, - TTable, - TWhere, - TSelect, - TInclude, - 1, - TOffset, - 'maybe', - TLazy, - TGroupBy - >( - this._from, - this._where, - this._select, - this._include, - 1, - this._offset, - 'maybe', - this._lazy, - this._groupBy, - this._name, - ).build(); + return this.cardinality('maybe').limit(1).build(); } select>(select: TSelect) { @@ -460,6 +367,19 @@ export class QueryBuilder< ); } + /** + * Marks the execution of the query to be deferred. + * + * This means that the parent query will be executed first and results will be sent + * to the client while the deferred query is executed in the background. + */ + defer() { + return this.lazy(); + } + + /** + * @alias {@link defer} + */ lazy() { return new QueryBuilder< DB, @@ -512,6 +432,34 @@ export class QueryBuilder< ); } + cardinality( + cardinality: TNewCardinality, + ) { + return new QueryBuilder< + DB, + TTable, + TWhere, + TSelect, + TInclude, + TLimit, + TOffset, + TNewCardinality, + TLazy, + TGroupBy + >( + this._from, + this._where, + this._select, + this._include, + this._limit, + this._offset, + cardinality, + this._lazy, + this._groupBy, + this._name, + ); + } + /** * Sets the name of the query. * @@ -554,8 +502,6 @@ export class QueryBuilder< export function query(schema: Schema) { return { from>(table: TTable) { - type TKeys = Array>; - const select = getTableSelectableColumns(schema, table); const primaryKeys = getTablePrimaryKeyColumns(schema, table); @@ -564,7 +510,7 @@ export function query(schema: Schema) { DB, TTable, {}, - { [k in TKeys[number]]: true }, + typeof select, {}, number | undefined, number | undefined, diff --git a/packages/queries/src/types/QueryResult.ts b/packages/queries/src/types/QueryResult.ts index 984a02d6..55b84636 100644 --- a/packages/queries/src/types/QueryResult.ts +++ b/packages/queries/src/types/QueryResult.ts @@ -5,7 +5,7 @@ import { Table } from './Table'; export type QueryResult = Simplify< TQuery extends Query - ? ApplyCardinality + ? ApplyDeferredQueryResult : never >; @@ -64,14 +64,18 @@ type QueryResultFromInclude< >; }; -type LazyQueryResult< +export type DeferredResult = + | { status: 'pending' } + | { status: 'done'; data: T } + | { status: 'error'; error: unknown }; + +type ApplyDeferredQueryResult< DB, TTable extends Table, TQuery extends Query, -> = - | { status: 'pending' } - | { status: 'done'; data: QueryResult } - | { status: 'error'; error: any }; +> = TQuery extends { lazy: true } + ? DeferredResult> + : ApplyCardinality; type MaybeQueryResult< DB, @@ -90,17 +94,22 @@ type ApplyCardinality< TTable extends Table, TQuery extends Query, > = - // Case 1: lazy query - TQuery extends { lazy: true } - ? LazyQueryResult - : // Case 2: many - TQuery extends { cardinality: 'many' } - ? ManyQueryResult - : // Case 2: one - TQuery extends { cardinality: 'one' } - ? QueryResultInner - : // Case 2: maybe - TQuery extends { cardinality: 'maybe' } - ? MaybeQueryResult - : // Else - never; + // prettier-ignore + // Case 1: many + TQuery extends { cardinality: 'many' } + ? ManyQueryResult + + : // Case 2: undefined cardinality + TQuery['cardinality'] extends undefined + ? ManyQueryResult + + : // Case 3: one + TQuery extends { cardinality: 'one' } + ? QueryResultInner + + : // Case 4: maybe + TQuery extends { cardinality: 'maybe' } + ? MaybeQueryResult + + : // Else + never;