import type { StorageTable } from '@prisma-next/sql-contract/types'; import type { AnyFromSource, TableSource } from '@prisma-next/sql-relational-core/ast'; import type { AggregateFunctions, Expression, ExpressionBuilder, ExtractScopeFields, FieldProxy, Functions, WithField, WithFields, } from '../expression'; import type { EmptyRow, Expand, JoinOuterScope, JoinSource, MergeScopes, NullableScope, QueryContext, RebindScope, Scope, ScopeField, ScopeTable, StorageTableToScopeTable, Subquery, } from '../scope'; import type { NamespaceTable, TableProxyContract } from '../types/db'; import type { JoinedTables } from '../types/joined-tables'; import type { DeleteQuery, InsertQuery, UpdateQuery } from '../types/mutation-query'; import type { SelectQuery } from '../types/select-query'; import type { LateralBuilder } from '../types/shared'; import type { TableProxy } from '../types/table-proxy'; import { BuilderBase, type BuilderContext, emptyState, tableToScope } from './builder-base'; import { JoinedTablesImpl } from './joined-tables-impl'; import { buildParamValues, buildSetExpressions, DeleteQueryImpl, evaluateUpdateCallback, InsertQueryImpl, UpdateQueryImpl, type UpdateSetCallback, } from './mutation-impl'; import { SelectQueryImpl } from './query-impl'; import { tableSourceForProxy } from './table-source-for-proxy'; export class TableProxyImpl< C extends TableProxyContract, Name extends string, Alias extends string, AvailableScope extends Scope, QC extends QueryContext, NsId extends string = string, > extends BuilderBase implements TableProxy { declare readonly [JoinOuterScope]: JoinSource< StorageTableToScopeTable>, Alias >[typeof JoinOuterScope]; readonly #tableName: string; readonly #table: StorageTable; readonly #namespaceId: string; readonly #fromSource: TableSource; readonly #scope: Scope; constructor( tableName: string, table: StorageTable, alias: string, ctx: BuilderContext, namespaceId: string, ) { super(ctx); this.#tableName = tableName; this.#table = table; this.#namespaceId = namespaceId; this.#scope = tableToScope(alias, table, { storage: ctx.storage, tableName, namespaceId, }); this.#fromSource = tableSourceForProxy(tableName, alias, namespaceId); } lateralJoin = this._gate( { sql: { lateral: true } }, 'lateralJoin', >( alias: LAlias, builder: (lateral: LateralBuilder) => Subquery, ): JoinedTables< QC, MergeScopes }> > => { return this.#toJoined().lateralJoin(alias, builder); }, ) as TableProxy['lateralJoin']; outerLateralJoin = this._gate( { sql: { lateral: true } }, 'outerLateralJoin', >( alias: LAlias, builder: (lateral: LateralBuilder) => Subquery, ): JoinedTables< QC, MergeScopes< AvailableScope, NullableScope<{ topLevel: LateralRow; namespaces: Record }> > > => { return this.#toJoined().outerLateralJoin(alias, builder); }, ) as TableProxy['outerLateralJoin']; getJoinOuterScope(): Scope { return this.#scope; } buildAst(): AnyFromSource { return this.#fromSource; } as( newAlias: NewAlias, ): TableProxy, QC> { return new TableProxyImpl< C, Name, NewAlias, RebindScope, QC, NsId >(this.#tableName, this.#table, newAlias, this.ctx, this.#namespaceId); } select( ...columns: Columns ): SelectQuery>; select( alias: LAlias, expr: (fields: FieldProxy, fns: AggregateFunctions) => Expression, ): SelectQuery>; select>>( callback: (fields: FieldProxy, fns: AggregateFunctions) => Result, ): SelectQuery>>; select(...args: unknown[]): unknown { return new SelectQueryImpl(emptyState(this.#fromSource, this.#scope), this.ctx).select( ...(args as string[]), ); } innerJoin>( other: Other, on: ExpressionBuilder, QC>, ): JoinedTables> { return this.#toJoined().innerJoin(other, on); } outerLeftJoin>( other: Other, on: ExpressionBuilder, QC>, ): JoinedTables>> { return this.#toJoined().outerLeftJoin(other, on); } outerRightJoin>( other: Other, on: ExpressionBuilder, QC>, ): JoinedTables, Other[typeof JoinOuterScope]>> { return this.#toJoined().outerRightJoin(other, on); } outerFullJoin>( other: Other, on: ExpressionBuilder, QC>, ): JoinedTables< QC, MergeScopes, NullableScope> > { return this.#toJoined().outerFullJoin(other, on); } insert(rows: ReadonlyArray>): InsertQuery { return new InsertQueryImpl( this.#fromSource, this.#namespaceId, this.#table, this.#scope, rows, this.ctx, ); } update( setOrCallback: | Record | (( fields: FieldProxy, fns: Functions, ) => Record | undefined>), ): UpdateQuery { if (typeof setOrCallback === 'function') { const callbackExprs = evaluateUpdateCallback( setOrCallback as UpdateSetCallback, this.#scope, this.ctx.queryOperationTypes, this.ctx.rawCodecInferer, ); const setExpressions = buildSetExpressions( callbackExprs, this.#namespaceId, this.#table, this.#tableName, 'update', this.ctx, ); return new UpdateQueryImpl(this.#fromSource, this.#scope, setExpressions, this.ctx); } const setExpressions = buildParamValues( setOrCallback, this.#namespaceId, this.#table, this.#tableName, 'update', this.ctx, ); return new UpdateQueryImpl(this.#fromSource, this.#scope, setExpressions, this.ctx); } delete(): DeleteQuery { return new DeleteQueryImpl(this.#fromSource, this.#scope, this.ctx); } #toJoined(): JoinedTables { return new JoinedTablesImpl(emptyState(this.#fromSource, this.#scope), this.ctx); } }