From 4a15249066e53ec6da8babaa6f5e202c074d3418 Mon Sep 17 00:00:00 2001 From: Diogo Rodrigues Date: Wed, 24 Jul 2024 14:40:53 +0100 Subject: [PATCH] Added attach --- drizzle-orm/src/singlestore-core/db.ts | 7 + drizzle-orm/src/singlestore-core/dialect.ts | 7 + .../singlestore-core/query-builders/attach.ts | 164 ++++++++++++++++++ 3 files changed, 178 insertions(+) create mode 100644 drizzle-orm/src/singlestore-core/query-builders/attach.ts diff --git a/drizzle-orm/src/singlestore-core/db.ts b/drizzle-orm/src/singlestore-core/db.ts index 652670ab4..76adbb32a 100644 --- a/drizzle-orm/src/singlestore-core/db.ts +++ b/drizzle-orm/src/singlestore-core/db.ts @@ -28,6 +28,7 @@ import type { import type { WithSubqueryWithSelection } from './subquery.ts'; import type { SingleStoreTable } from './table.ts'; import { SingleStoreDetachBase } from './query-builders/detach.ts'; +import { SingleStoreAttachBase } from './query-builders/attach.ts'; export class SingleStoreDatabase< TQueryResult extends SingleStoreQueryResultHKT, @@ -480,6 +481,12 @@ export class SingleStoreDatabase< ): SingleStoreDetachBase { return new SingleStoreDetachBase(database, this.session, this.dialect); } + + attach( + database: TDatabase, + ): SingleStoreAttachBase { + return new SingleStoreAttachBase(database, this.session, this.dialect); + } } export type SingleStoreWithReplicas = Q & { $primary: Q }; diff --git a/drizzle-orm/src/singlestore-core/dialect.ts b/drizzle-orm/src/singlestore-core/dialect.ts index 7309e78c3..c9bfda5f2 100644 --- a/drizzle-orm/src/singlestore-core/dialect.ts +++ b/drizzle-orm/src/singlestore-core/dialect.ts @@ -35,6 +35,7 @@ import type { SingleStoreSession } from './session.ts'; import { SingleStoreTable } from './table.ts'; import { SingleStoreViewBase } from './view-base.ts'; import type { SingleStoreDetachConfig } from './query-builders/detach.ts'; +import type { SingleStoreAttachConfig } from './query-builders/attach.ts'; export class SingleStoreDialect { static readonly [entityKind]: string = 'SingleStoreDialect'; @@ -125,6 +126,12 @@ export class SingleStoreDialect { return sql`detach database ${database}${milestoneSql}${workspaceSql}`; } + buildAttachQuery({ database, milestone }: SingleStoreAttachConfig): SQL { + const milestoneSql = milestone ? sql` at milestone ${milestone}` : undefined; + + return sql`attach database ${database}${milestoneSql}`; + } + buildUpdateSet(table: SingleStoreTable, set: UpdateSet): SQL { const tableColumns = table[Table.Symbol.Columns]; diff --git a/drizzle-orm/src/singlestore-core/query-builders/attach.ts b/drizzle-orm/src/singlestore-core/query-builders/attach.ts new file mode 100644 index 000000000..cd26a73b6 --- /dev/null +++ b/drizzle-orm/src/singlestore-core/query-builders/attach.ts @@ -0,0 +1,164 @@ +import { entityKind } from '~/entity.ts'; +import { QueryPromise } from '~/query-promise.ts'; +import type { SingleStoreDialect } from '~/singlestore-core/dialect.ts'; +import type { + AnySingleStoreQueryResultHKT, + PreparedQueryHKTBase, + PreparedQueryKind, + SingleStorePreparedQueryConfig, + SingleStoreQueryResultHKT, + SingleStoreQueryResultKind, + SingleStoreSession, +} from '~/singlestore-core/session.ts'; +import type { Query, SQL, SQLWrapper } from '~/sql/sql.ts'; + +export type SingleStoreAttachWithout< + T extends AnySingleStoreAttachBase, + TDynamic extends boolean, + K extends keyof T & string, +> = TDynamic extends true ? T + : Omit< + SingleStoreAttachBase< + T['_']['database'], + T['_']['queryResult'], + T['_']['preparedQueryHKT'], + TDynamic, + T['_']['excludedMethods'] | K + >, + T['_']['excludedMethods'] | K + >; + +export type SingleStoreAttach< + TDatabase extends string = string, + TQueryResult extends SingleStoreQueryResultHKT = AnySingleStoreQueryResultHKT, + TPreparedQueryHKT extends PreparedQueryHKTBase = PreparedQueryHKTBase, +> = SingleStoreAttachBase; + +export interface SingleStoreAttachConfig { + milestone?: string | undefined; + database: string; +} + +export type SingleStoreAttachPrepare = PreparedQueryKind< + T['_']['preparedQueryHKT'], + SingleStorePreparedQueryConfig & { + execute: SingleStoreQueryResultKind; + iterator: never; + }, + true +>; + +type SingleStoreAttachDynamic = SingleStoreAttach< + T['_']['database'], + T['_']['queryResult'], + T['_']['preparedQueryHKT'] +>; + +type AnySingleStoreAttachBase = SingleStoreAttachBase; + +export interface SingleStoreAttachBase< + TDatabase extends string, + TQueryResult extends SingleStoreQueryResultHKT, + TPreparedQueryHKT extends PreparedQueryHKTBase, + TDynamic extends boolean = false, + TExcludedMethods extends string = never, +> extends QueryPromise> { + readonly _: { + readonly database: TDatabase; + readonly queryResult: TQueryResult; + readonly preparedQueryHKT: TPreparedQueryHKT; + readonly dynamic: TDynamic; + readonly excludedMethods: TExcludedMethods; + }; +} + +export class SingleStoreAttachBase< + TDatabase extends string, + TQueryResult extends SingleStoreQueryResultHKT, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + TPreparedQueryHKT extends PreparedQueryHKTBase, + TDynamic extends boolean = false, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + TExcludedMethods extends string = never, +> extends QueryPromise> implements SQLWrapper { + static readonly [entityKind]: string = 'SingleStoreAttach'; + + private config: SingleStoreAttachConfig; + + constructor( + private database: TDatabase, + private session: SingleStoreSession, + private dialect: SingleStoreDialect, + ) { + super(); + this.config = { database }; + } + + /** + * Adds a `where` clause to the query. + * + * Calling this method will delete only those rows that fulfill a specified condition. + * + * See docs: {@link https://orm.drizzle.team/docs/delete} + * + * @param where the `where` clause. + * + * @example + * You can use conditional operators and `sql function` to filter the rows to be deleted. + * + * ```ts + * // Attach all cars with green color + * db.delete(cars).where(eq(cars.color, 'green')); + * // or + * db.delete(cars).where(sql`${cars.color} = 'green'`) + * ``` + * + * You can logically combine conditional operators with `and()` and `or()` operators: + * + * ```ts + * // Attach all BMW cars with a green color + * db.delete(cars).where(and(eq(cars.color, 'green'), eq(cars.brand, 'BMW'))); + * + * // Attach all cars with the green or blue color + * db.delete(cars).where(or(eq(cars.color, 'green'), eq(cars.color, 'blue'))); + * ``` + */ + atMilestone(milestone: string | undefined): SingleStoreAttachWithout { + this.config.milestone = milestone; + return this as any; + } + + /** @internal */ + getSQL(): SQL { + return this.dialect.buildAttachQuery(this.config); + } + + toSQL(): Query { + const { typings: _typings, ...rest } = this.dialect.sqlToQuery(this.getSQL()); + return rest; + } + + prepare(): SingleStoreAttachPrepare { + return this.session.prepareQuery( + this.dialect.sqlToQuery(this.getSQL()), + undefined, + ) as SingleStoreAttachPrepare; + } + + override execute: ReturnType['execute'] = (placeholderValues) => { + return this.prepare().execute(placeholderValues); + }; + + private createIterator = (): ReturnType['iterator'] => { + const self = this; + return async function*(placeholderValues) { + yield* self.prepare().iterator(placeholderValues); + }; + }; + + iterator = this.createIterator(); + + $dynamic(): SingleStoreAttachDynamic { + return this as any; + } +}