From 2dd6c35f08070237f1817a43490313dbc8e6b277 Mon Sep 17 00:00:00 2001 From: Ben Merckx Date: Fri, 26 Apr 2024 11:21:54 +0200 Subject: [PATCH] Interim --- src/core/Builder.ts | 26 ++++++++++++++++++++-- src/core/Emitter.ts | 18 +++++++++++++--- src/core/MetaData.ts | 8 +++++++ src/core/query/Insert.ts | 13 ++++++++--- src/core/query/Select.ts | 33 +++++++++++++++++++++++++++- src/core/query/Union.ts | 44 +++++++++++++++++++++++++++++++++++++- src/core/query/Update.ts | 8 ++++--- test/driver/pglite.test.ts | 10 +++++---- 8 files changed, 143 insertions(+), 17 deletions(-) diff --git a/src/core/Builder.ts b/src/core/Builder.ts index c8fc95a..01944cf 100644 --- a/src/core/Builder.ts +++ b/src/core/Builder.ts @@ -1,5 +1,5 @@ -import {getData, internalData, type HasTable} from './Internal.ts' -import type {QueryMeta} from './MetaData.ts' +import {getData, internalData, type HasSql, type HasTable} from './Internal.ts' +import type {IsPostgres, QueryMeta} from './MetaData.ts' import type {QueryData} from './Query.ts' import type {SelectionInput} from './Selection.ts' import type {Table, TableDefinition} from './Table.ts' @@ -59,6 +59,28 @@ export class Builder { }) } + selectDistinctOn( + this: Builder, + columns: Array + ): WithoutSelection + selectDistinctOn( + this: Builder, + columns: Array, + selection: Input + ): WithSelection + selectDistinctOn(columns: any, selection?: any): any { + return new Select({ + ...getData(this), + select: { + type: selection ? 'selection' : 'allFrom', + input: selection, + tables: [], + nullable: [] + }, + distinctOn: columns + }) + } + update(table: Table) { return new UpdateTable({...getData(this), table}) } diff --git a/src/core/Emitter.ts b/src/core/Emitter.ts index c23311a..f63fe02 100644 --- a/src/core/Emitter.ts +++ b/src/core/Emitter.ts @@ -108,12 +108,24 @@ export abstract class Emitter { } emitSelect(select: Select): void { - const {from, distinct, where, groupBy, orderBy, having, limit, offset} = - getData(select) + const { + from, + distinct, + distinctOn, + where, + groupBy, + orderBy, + having, + limit, + offset + } = getData(select) const selected = getSelection(select) + const prefix = distinctOn + ? sql`distinct on (${sql.join(distinctOn, sql`, `)})` + : distinct && sql`distinct` sql .query({ - select: distinct ? sql`distinct ${selected}` : selected, + select: sql.join([prefix, selected]), from, where, groupBy, diff --git a/src/core/MetaData.ts b/src/core/MetaData.ts index 7400a94..7b6b0eb 100644 --- a/src/core/MetaData.ts +++ b/src/core/MetaData.ts @@ -22,6 +22,14 @@ export interface IsPostgres extends QueryMeta { dialect: 'postgres' } +export interface IsMysql extends QueryMeta { + dialect: 'mysql' +} + +export interface IsSqlite extends QueryMeta { + dialect: 'sqlite' +} + export interface Either extends QueryMeta { mode: 'sync' | 'async' dialect: QueryDialect diff --git a/src/core/query/Insert.ts b/src/core/query/Insert.ts index a818151..5eeffcc 100644 --- a/src/core/query/Insert.ts +++ b/src/core/query/Insert.ts @@ -11,7 +11,12 @@ import { } from '../Internal.ts' import type {IsPostgres, QueryMeta} from '../MetaData.ts' import {Query, QueryData} from '../Query.ts' -import {selection, type Selection} from '../Selection.ts' +import { + selection, + type Selection, + type SelectionInput, + type SelectionRow +} from '../Selection.ts' import {sql} from '../Sql.ts' import type { TableDefinition, @@ -53,8 +58,10 @@ class InsertCanReturn< Meta extends QueryMeta > extends Insert { returning(): Insert, Meta> - returning(returning: HasSql): Insert - returning(returning?: HasSql) { + returning( + returning: Input + ): Insert, Meta> + returning(returning?: SelectionInput) { const data = getData(this) return new Insert({ ...data, diff --git a/src/core/query/Select.ts b/src/core/query/Select.ts index 2098b1e..d4f71d8 100644 --- a/src/core/query/Select.ts +++ b/src/core/query/Select.ts @@ -15,7 +15,7 @@ import { type HasSql, type HasTable } from '../Internal.ts' -import type {QueryMeta} from '../MetaData.ts' +import type {IsMysql, IsPostgres, QueryMeta} from '../MetaData.ts' import {Query, QueryData} from '../Query.ts' import { selection, @@ -41,6 +41,7 @@ export class SelectData extends QueryData { input?: SelectionInput } distinct?: boolean + distinctOn?: Array from?: HasSql subject?: HasSql where?: HasSql @@ -205,6 +206,19 @@ export class Select }) } + intersectAll( + this: SelectBase, + right: SelectBase + ): Union { + return new Union({ + ...getData(>this), + selection: getSelection(this), + left: this, + operator: sql`intersect all`, + right + }) + } + except(right: SelectBase): Union { return new Union({ ...getData(this), @@ -215,6 +229,18 @@ export class Select }) } + exceptAll( + right: SelectBase + ): Union { + return new Union({ + ...getData(>this), + selection: getSelection(this), + left: this, + operator: sql`except all`, + right + }) + } + get [internalSelection]() { const {select} = getData(this) if (!select.input) throw new Error('No selection defined') @@ -240,7 +266,12 @@ export interface SelectBase union(right: SelectBase): Union unionAll(right: SelectBase): Union intersect(right: SelectBase): Union + intersectAll( + this: SelectBase, + right: SelectBase + ): Union except(right: SelectBase): Union + exceptAll(right: SelectBase): Union as(name: string): SubQuery } diff --git a/src/core/query/Union.ts b/src/core/query/Union.ts index 714f19d..af2f6b1 100644 --- a/src/core/query/Union.ts +++ b/src/core/query/Union.ts @@ -7,7 +7,7 @@ import { type HasSelection, type HasSql } from '../Internal.ts' -import type {QueryMeta} from '../MetaData.ts' +import type {IsMysql, IsPostgres, QueryMeta} from '../MetaData.ts' import {Query, QueryData} from '../Query.ts' import type {Selection} from '../Selection.ts' import {sql} from '../Sql.ts' @@ -73,3 +73,45 @@ export class Union return sql.chunk('emitUnion', this) } } + +export function union( + left: SelectBase, + right: SelectBase +): Union { + return left.union(right) +} + +export function unionAll( + left: SelectBase, + right: SelectBase +): Union { + return left.unionAll(right) +} + +export function intersect( + left: SelectBase, + right: SelectBase +): Union { + return left.intersect(right) +} + +export function intersectAll( + left: SelectBase, + right: SelectBase +): Union { + return left.intersectAll(right) +} + +export function except( + left: SelectBase, + right: SelectBase +): Union { + return left.except(right) +} + +export function exceptAll( + left: SelectBase, + right: SelectBase +): Union { + return left.exceptAll(right) +} diff --git a/src/core/query/Update.ts b/src/core/query/Update.ts index f4f9047..3293586 100644 --- a/src/core/query/Update.ts +++ b/src/core/query/Update.ts @@ -49,7 +49,7 @@ export class UpdateTable< Definition extends TableDefinition, Meta extends QueryMeta > extends Update { - set(values: TableUpdate) { + set(values: TableUpdate): UpdateTable { const set = sql.join( Object.entries(values).map( ([key, value]) => sql`${sql.identifier(key)} = ${input(value)}` @@ -59,11 +59,13 @@ export class UpdateTable< return new UpdateTable({...getData(this), set}) } - where(where: HasSql) { + where(where: HasSql): UpdateTable { return new UpdateTable({...getData(this), where}) } - returning(returning: Input) { + returning( + returning: Input + ): Update, Meta> { return new Update, Meta>({ ...getData(this), returning: selection(returning) diff --git a/test/driver/pglite.test.ts b/test/driver/pglite.test.ts index 39858ca..fbe869a 100644 --- a/test/driver/pglite.test.ts +++ b/test/driver/pglite.test.ts @@ -1,7 +1,9 @@ +import {Os} from '@sinclair/carbon' import {connect} from '../../src/driver/pglite.ts' import {testDriver} from '../TestDriver.ts' -await testDriver('pglite', async () => { - const {PGlite} = await import('@electric-sql/pglite') - return connect(new PGlite()) -}) +if (Os.type() !== 'win32') + await testDriver('pglite', async () => { + const {PGlite} = await import('@electric-sql/pglite') + return connect(new PGlite()) + })