metal-orm 1.1.23 → 1.1.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/index.cjs +832 -583
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +281 -144
  4. package/dist/index.d.ts +281 -144
  5. package/dist/index.js +807 -583
  6. package/dist/index.js.map +1 -1
  7. package/package.json +1 -1
  8. package/src/core/dialect/base/returning-strategy.ts +40 -39
  9. package/src/core/dialect/base/sql-compiler-set.ts +33 -0
  10. package/src/core/dialect/base/sql-dialect.ts +125 -222
  11. package/src/core/dialect/base/standard-delete-compiler.ts +37 -0
  12. package/src/core/dialect/base/standard-insert-compiler.ts +49 -0
  13. package/src/core/dialect/base/standard-select-compiler.ts +82 -0
  14. package/src/core/dialect/base/standard-sql-services.ts +44 -0
  15. package/src/core/dialect/base/standard-sql-source-compiler.ts +88 -0
  16. package/src/core/dialect/base/standard-update-compiler.ts +53 -0
  17. package/src/core/dialect/base/upsert-strategy.ts +45 -0
  18. package/src/core/dialect/capabilities/procedure-compiler.ts +10 -8
  19. package/src/core/dialect/mssql/compiler-factory.ts +12 -0
  20. package/src/core/dialect/mssql/delete-compiler.ts +40 -0
  21. package/src/core/dialect/mssql/index.ts +24 -371
  22. package/src/core/dialect/mssql/insert-compiler.ts +112 -0
  23. package/src/core/dialect/mssql/output.ts +46 -0
  24. package/src/core/dialect/mssql/procedure-compiler.ts +81 -0
  25. package/src/core/dialect/mssql/select-compiler.ts +116 -0
  26. package/src/core/dialect/mssql/update-compiler.ts +37 -0
  27. package/src/core/dialect/mysql/index.ts +24 -117
  28. package/src/core/dialect/mysql/procedure-compiler.ts +67 -0
  29. package/src/core/dialect/mysql/upsert.ts +42 -0
  30. package/src/core/dialect/postgres/index.ts +34 -101
  31. package/src/core/dialect/postgres/procedure-compiler.ts +41 -0
  32. package/src/core/dialect/postgres/returning.ts +4 -0
  33. package/src/core/dialect/postgres/upsert.ts +43 -0
  34. package/src/core/dialect/sqlite/index.ts +15 -70
  35. package/src/core/dialect/sqlite/returning.ts +30 -0
  36. package/src/core/dialect/sqlite/upsert.ts +43 -0
  37. package/src/index.ts +28 -10
@@ -0,0 +1,82 @@
1
+ import type { CompilerContext } from '../abstract.js';
2
+ import type { SelectQueryNode } from '../../ast/query.js';
3
+ import { CteCompiler } from './cte-compiler.js';
4
+ import { JoinCompiler } from './join-compiler.js';
5
+ import { GroupByCompiler } from './groupby-compiler.js';
6
+ import { OrderByCompiler } from './orderby-compiler.js';
7
+ import { StandardSqlSourceCompiler } from './standard-sql-source-compiler.js';
8
+ import type { StandardSqlCompilerServices } from './standard-sql-services.js';
9
+
10
+ /** Standard SELECT orchestration, independent from any dialect class hierarchy. */
11
+ export class StandardSelectCompiler {
12
+ public constructor(
13
+ private readonly services: StandardSqlCompilerServices,
14
+ private readonly sources: StandardSqlSourceCompiler
15
+ ) {}
16
+
17
+ compile(ast: SelectQueryNode, ctx: CompilerContext): string {
18
+ const hasSetOps = !!(ast.setOps && ast.setOps.length);
19
+ const ctes = CteCompiler.compileCtes(
20
+ ast,
21
+ ctx,
22
+ id => this.services.quoteIdentifier(id),
23
+ (query, compilerContext) => this.services.compileSelectAst(query, compilerContext),
24
+ query => this.services.normalizeSelectAst(query),
25
+ sql => this.sources.stripTrailingSemicolon(sql)
26
+ );
27
+
28
+ const baseAst: SelectQueryNode = hasSetOps
29
+ ? { ...ast, setOps: undefined, orderBy: undefined, limit: undefined, offset: undefined }
30
+ : ast;
31
+ const baseSelect = this.compileCore(baseAst, ctx);
32
+
33
+ if (!hasSetOps) return `${ctes}${baseSelect}`;
34
+
35
+ const compound = ast.setOps!
36
+ .map(op => `${op.operator} ${this.sources.wrapSetOperand(this.services.compileSelectAst(op.query, ctx))}`)
37
+ .join(' ');
38
+ const orderBy = this.compileOrderBy(ast, ctx);
39
+ const pagination = this.services.getPaginationStrategy().compilePagination(ast.limit, ast.offset);
40
+ const combined = `${this.sources.wrapSetOperand(baseSelect)} ${compound}`;
41
+ return `${ctes}${combined}${orderBy}${pagination}`;
42
+ }
43
+
44
+ private compileCore(ast: SelectQueryNode, ctx: CompilerContext): string {
45
+ const columns = this.compileColumns(ast, ctx);
46
+ const from = this.sources.compileFrom(ast.from, ctx);
47
+ const joins = JoinCompiler.compileJoins(
48
+ ast.joins,
49
+ ctx,
50
+ (source, compilerContext) => this.sources.compileFrom(source, compilerContext),
51
+ (expression, compilerContext) => this.services.compileExpression(expression, compilerContext)
52
+ );
53
+ const where = ast.where ? ` WHERE ${this.services.compileExpression(ast.where, ctx)}` : '';
54
+ const groupBy = GroupByCompiler.compileGroupBy(
55
+ ast,
56
+ term => this.services.compileOrderingTerm(term, ctx)
57
+ );
58
+ const having = ast.having ? ` HAVING ${this.services.compileExpression(ast.having, ctx)}` : '';
59
+ const orderBy = this.compileOrderBy(ast, ctx);
60
+ const pagination = this.services.getPaginationStrategy().compilePagination(ast.limit, ast.offset);
61
+ return `SELECT ${ast.distinct ? 'DISTINCT ' : ''}${columns} FROM ${from}${joins}${where}${groupBy}${having}${orderBy}${pagination}`;
62
+ }
63
+
64
+ private compileColumns(ast: SelectQueryNode, ctx: CompilerContext): string {
65
+ if (!ast.columns || ast.columns.length === 0) return '*';
66
+ return ast.columns.map(column => {
67
+ const expr = this.services.compileOperand(column, ctx);
68
+ if (!column.alias) return expr;
69
+ if (column.alias.includes('(')) return column.alias;
70
+ return `${expr} AS ${this.services.quoteIdentifier(column.alias)}`;
71
+ }).join(', ');
72
+ }
73
+
74
+ private compileOrderBy(ast: SelectQueryNode, ctx: CompilerContext): string {
75
+ return OrderByCompiler.compileOrderBy(
76
+ ast,
77
+ term => this.services.compileOrderingTerm(term, ctx),
78
+ order => this.services.renderOrderByNulls(order),
79
+ order => this.services.renderOrderByCollation(order)
80
+ );
81
+ }
82
+ }
@@ -0,0 +1,44 @@
1
+ import type { CompilerContext } from '../abstract.js';
2
+ import type {
3
+ SelectQueryNode,
4
+ InsertQueryNode,
5
+ OrderByNode,
6
+ OrderingTerm,
7
+ TableNode
8
+ } from '../../ast/query.js';
9
+ import type {
10
+ ColumnNode,
11
+ ExpressionNode,
12
+ OperandNode
13
+ } from '../../ast/expression.js';
14
+ import type { DialectName } from '../../sql/sql.js';
15
+ import type { PaginationStrategy } from './pagination-strategy.js';
16
+ import type { TableFunctionStrategy } from '../../functions/table-types.js';
17
+
18
+ /**
19
+ * Narrow callback surface consumed by the standard SQL compilers.
20
+ *
21
+ * The compilers deliberately know nothing about SqlDialectBase or any concrete
22
+ * backend class. A dialect can assemble these services through inheritance,
23
+ * composition, or a plain object.
24
+ */
25
+ export interface StandardSqlCompilerServices {
26
+ getDialectName(): DialectName;
27
+ getPaginationStrategy(): PaginationStrategy;
28
+ getTableFunctionStrategy(): TableFunctionStrategy;
29
+
30
+ quoteIdentifier(id: string): string;
31
+ compileOperand(node: OperandNode, ctx: CompilerContext): string;
32
+ compileExpression(node: ExpressionNode, ctx: CompilerContext): string;
33
+ compileOrderingTerm(term: OrderingTerm, ctx: CompilerContext): string;
34
+
35
+ normalizeSelectAst(ast: SelectQueryNode): SelectQueryNode;
36
+ compileSelectAst(ast: SelectQueryNode, ctx: CompilerContext): string;
37
+
38
+ compileReturning(returning: ColumnNode[] | undefined, ctx: CompilerContext): string;
39
+ compileUpsertClause(ast: InsertQueryNode, ctx: CompilerContext): string;
40
+ compileSetTarget(column: ColumnNode, table: TableNode): string;
41
+
42
+ renderOrderByNulls(order: OrderByNode): string | undefined;
43
+ renderOrderByCollation(order: OrderByNode): string | undefined;
44
+ }
@@ -0,0 +1,88 @@
1
+ import type { CompilerContext } from '../abstract.js';
2
+ import type {
3
+ DerivedTableNode,
4
+ FunctionTableNode,
5
+ TableSourceNode
6
+ } from '../../ast/query.js';
7
+ import { FunctionTableFormatter } from './function-table-formatter.js';
8
+ import type { StandardSqlCompilerServices } from './standard-sql-services.js';
9
+
10
+ /** Shared FROM/table-source rendering used by the standard query compilers. */
11
+ export class StandardSqlSourceCompiler {
12
+ public constructor(private readonly services: StandardSqlCompilerServices) {}
13
+
14
+ compileFrom(source: TableSourceNode, ctx?: CompilerContext): string {
15
+ if (source.type === 'FunctionTable') return this.compileFunctionTable(source, ctx);
16
+ if (source.type === 'DerivedTable') return this.compileDerivedTable(source, ctx);
17
+ return this.compileTableSource(source);
18
+ }
19
+
20
+ compileFunctionTable(fn: FunctionTableNode, ctx?: CompilerContext): string {
21
+ const key = fn.key ?? fn.name;
22
+
23
+ if (ctx) {
24
+ const renderer = this.services.getTableFunctionStrategy().getRenderer(key);
25
+ if (renderer) {
26
+ const compiledArgs = (fn.args ?? []).map(arg => this.services.compileOperand(arg, ctx));
27
+ return renderer({
28
+ node: fn,
29
+ compiledArgs,
30
+ compileOperand: operand => this.services.compileOperand(operand, ctx),
31
+ quoteIdentifier: id => this.services.quoteIdentifier(id)
32
+ });
33
+ }
34
+
35
+ if (fn.key) {
36
+ throw new Error(
37
+ `Table function "${key}" is not supported by dialect "${this.services.getDialectName()}".`
38
+ );
39
+ }
40
+ }
41
+
42
+ return FunctionTableFormatter.format(fn, ctx, {
43
+ quoteIdentifier: id => this.services.quoteIdentifier(id),
44
+ compileOperand: (node, compilerContext) => this.services.compileOperand(node, compilerContext)
45
+ });
46
+ }
47
+
48
+ compileDerivedTable(table: DerivedTableNode, ctx?: CompilerContext): string {
49
+ if (!table.alias) throw new Error('Derived tables must have an alias.');
50
+ if (!ctx) throw new Error('Derived table compilation requires a compiler context.');
51
+
52
+ const normalized = this.services.normalizeSelectAst(table.query);
53
+ const subquery = this.services.compileSelectAst(normalized, ctx).trim().replace(/;$/, '');
54
+ const columns = table.columnAliases?.length
55
+ ? ` (${table.columnAliases.map(column => this.services.quoteIdentifier(column)).join(', ')})`
56
+ : '';
57
+ return `(${subquery}) AS ${this.services.quoteIdentifier(table.alias)}${columns}`;
58
+ }
59
+
60
+ compileTableSource(table: TableSourceNode): string {
61
+ if (table.type === 'FunctionTable') return this.compileFunctionTable(table);
62
+ if (table.type === 'DerivedTable') {
63
+ throw new Error('Derived table compilation requires a compiler context.');
64
+ }
65
+ const base = this.compileTableName(table);
66
+ return table.alias ? `${base} AS ${this.services.quoteIdentifier(table.alias)}` : base;
67
+ }
68
+
69
+ compileTableName(table: { name: string; schema?: string }): string {
70
+ if (table.schema) {
71
+ return `${this.services.quoteIdentifier(table.schema)}.${this.services.quoteIdentifier(table.name)}`;
72
+ }
73
+ return this.services.quoteIdentifier(table.name);
74
+ }
75
+
76
+ compileTableReference(table: { name: string; schema?: string; alias?: string }): string {
77
+ const base = this.compileTableName(table);
78
+ return table.alias ? `${base} AS ${this.services.quoteIdentifier(table.alias)}` : base;
79
+ }
80
+
81
+ stripTrailingSemicolon(sql: string): string {
82
+ return sql.trim().replace(/;$/, '');
83
+ }
84
+
85
+ wrapSetOperand(sql: string): string {
86
+ return `(${this.stripTrailingSemicolon(sql)})`;
87
+ }
88
+ }
@@ -0,0 +1,53 @@
1
+ import type { CompilerContext } from '../abstract.js';
2
+ import type { UpdateQueryNode, TableNode } from '../../ast/query.js';
3
+ import type { ColumnNode, OperandNode } from '../../ast/expression.js';
4
+ import { JoinCompiler } from './join-compiler.js';
5
+ import { StandardSqlSourceCompiler } from './standard-sql-source-compiler.js';
6
+ import type { StandardSqlCompilerServices } from './standard-sql-services.js';
7
+
8
+ /** Standard UPDATE orchestration, independent from concrete dialect classes. */
9
+ export class StandardUpdateCompiler {
10
+ public constructor(
11
+ private readonly services: StandardSqlCompilerServices,
12
+ private readonly sources: StandardSqlSourceCompiler
13
+ ) {}
14
+
15
+ compile(ast: UpdateQueryNode, ctx: CompilerContext): string {
16
+ const target = this.sources.compileTableReference(ast.table);
17
+ const assignments = this.compileAssignments(ast.set, ast.table, ctx);
18
+ const from = this.compileFromClause(ast, ctx);
19
+ const where = ast.where ? ` WHERE ${this.services.compileExpression(ast.where, ctx)}` : '';
20
+ const returning = this.services.compileReturning(ast.returning, ctx);
21
+ return `UPDATE ${target} SET ${assignments}${from}${where}${returning}`;
22
+ }
23
+
24
+ compileAssignments(
25
+ assignments: { column: ColumnNode; value: OperandNode }[],
26
+ table: TableNode,
27
+ ctx: CompilerContext
28
+ ): string {
29
+ return assignments
30
+ .map(assignment => {
31
+ const target = this.services.compileSetTarget(assignment.column, table);
32
+ const value = this.services.compileOperand(assignment.value, ctx);
33
+ return `${target} = ${value}`;
34
+ })
35
+ .join(', ');
36
+ }
37
+
38
+ private compileFromClause(ast: UpdateQueryNode, ctx: CompilerContext): string {
39
+ if (!ast.from && (!ast.joins || ast.joins.length === 0)) return '';
40
+ if (!ast.from) {
41
+ throw new Error('UPDATE with JOINs requires an explicit FROM clause.');
42
+ }
43
+
44
+ const from = this.sources.compileFrom(ast.from, ctx);
45
+ const joins = JoinCompiler.compileJoins(
46
+ ast.joins,
47
+ ctx,
48
+ (source, compilerContext) => this.sources.compileFrom(source, compilerContext),
49
+ (expression, compilerContext) => this.services.compileExpression(expression, compilerContext)
50
+ );
51
+ return ` FROM ${from}${joins}`;
52
+ }
53
+ }
@@ -0,0 +1,45 @@
1
+ import type { CompilerContext } from '../abstract.js';
2
+ import type {
3
+ InsertQueryNode,
4
+ TableNode,
5
+ UpdateAssignmentNode
6
+ } from '../../ast/query.js';
7
+ import type { ExpressionNode, OperandNode } from '../../ast/expression.js';
8
+ import type { DialectName } from '../../sql/sql.js';
9
+
10
+ /** Narrow services needed by backend-specific UPSERT implementations. */
11
+ export interface UpsertCompilationServices {
12
+ getDialectName(): DialectName;
13
+ quoteIdentifier(id: string): string;
14
+ compileOperand(node: OperandNode, ctx: CompilerContext): string;
15
+ compileExpression(node: ExpressionNode, ctx: CompilerContext): string;
16
+ compileUpdateAssignments(
17
+ assignments: UpdateAssignmentNode[],
18
+ table: TableNode,
19
+ ctx: CompilerContext
20
+ ): string;
21
+ }
22
+
23
+ /** Backend-specific INSERT conflict/upsert rendering strategy. */
24
+ export interface UpsertStrategy {
25
+ compile(
26
+ ast: InsertQueryNode,
27
+ ctx: CompilerContext,
28
+ services: UpsertCompilationServices
29
+ ): string;
30
+ }
31
+
32
+ /** Default strategy for dialects without UPSERT support. */
33
+ export class NoUpsertStrategy implements UpsertStrategy {
34
+ compile(
35
+ ast: InsertQueryNode,
36
+ _ctx: CompilerContext,
37
+ services: UpsertCompilationServices
38
+ ): string {
39
+ void _ctx;
40
+ if (!ast.onConflict) return '';
41
+ throw new Error(
42
+ `UPSERT/ON CONFLICT is not supported by dialect "${services.getDialectName()}".`
43
+ );
44
+ }
45
+ }
@@ -1,5 +1,6 @@
1
1
  import type { ProcedureCallNode } from '../../ast/procedure.js';
2
- import type { CompiledQuery } from '../abstract.js';
2
+ import type { OperandNode } from '../../ast/expression.js';
3
+ import type { CompiledQuery, CompilerContext } from '../abstract.js';
3
4
 
4
5
  export interface CompiledProcedureCall extends CompiledQuery {
5
6
  outParams: {
@@ -8,13 +9,14 @@ export interface CompiledProcedureCall extends CompiledQuery {
8
9
  };
9
10
  }
10
11
 
11
- /**
12
- * Optional dialect capability for stored-procedure compilation.
13
- *
14
- * Dialects that do not support procedures simply do not implement this
15
- * interface; unsupported behavior is resolved at the capability boundary
16
- * rather than through mandatory methods that only throw.
17
- */
12
+ /** Narrow SQL services consumed by reusable procedure compiler components. */
13
+ export interface ProcedureCompilerServices {
14
+ quoteIdentifier(id: string): string;
15
+ createCompilerContext(): CompilerContext;
16
+ compileOperand(node: OperandNode, ctx: CompilerContext): string;
17
+ }
18
+
19
+ /** Optional dialect capability for stored-procedure compilation. */
18
20
  export interface ProcedureCompiler {
19
21
  compileProcedureCall(ast: ProcedureCallNode): CompiledProcedureCall;
20
22
  }
@@ -0,0 +1,12 @@
1
+ import type { SqlCompilerFactory } from '../base/sql-compiler-set.js';
2
+ import { MssqlDeleteCompiler } from './delete-compiler.js';
3
+ import { MssqlInsertCompiler } from './insert-compiler.js';
4
+ import { MssqlSelectCompiler } from './select-compiler.js';
5
+ import { MssqlUpdateCompiler } from './update-compiler.js';
6
+
7
+ export const createMssqlCompilerSet: SqlCompilerFactory = ({ services, sources }) => ({
8
+ select: new MssqlSelectCompiler(services, sources),
9
+ insert: new MssqlInsertCompiler(services, sources),
10
+ update: new MssqlUpdateCompiler(services, sources),
11
+ delete: new MssqlDeleteCompiler(services, sources)
12
+ });
@@ -0,0 +1,40 @@
1
+ import type { DeleteQueryNode } from '../../ast/query.js';
2
+ import type { CompilerContext } from '../abstract.js';
3
+ import { JoinCompiler } from '../base/join-compiler.js';
4
+ import type { SqlAstCompiler } from '../base/sql-compiler-set.js';
5
+ import type { StandardSqlCompilerServices } from '../base/standard-sql-services.js';
6
+ import type { StandardSqlSourceCompiler } from '../base/standard-sql-source-compiler.js';
7
+ import { MssqlOutputStrategy } from './output.js';
8
+
9
+ export class MssqlDeleteCompiler implements SqlAstCompiler<DeleteQueryNode> {
10
+ private readonly output = new MssqlOutputStrategy();
11
+
12
+ constructor(
13
+ private readonly services: StandardSqlCompilerServices,
14
+ private readonly sources: StandardSqlSourceCompiler
15
+ ) {}
16
+
17
+ compile(ast: DeleteQueryNode, ctx: CompilerContext): string {
18
+ if (ast.using) {
19
+ throw new Error('DELETE ... USING is not supported in the MSSQL dialect; use join() instead.');
20
+ }
21
+
22
+ const alias = ast.from.alias ?? ast.from.name;
23
+ const target = this.sources.compileTableReference(ast.from);
24
+ const joins = JoinCompiler.compileJoins(
25
+ ast.joins,
26
+ ctx,
27
+ (source, compilerContext) => this.sources.compileFrom(source, compilerContext),
28
+ (expression, compilerContext) => this.services.compileExpression(expression, compilerContext)
29
+ );
30
+ const where = ast.where
31
+ ? ` WHERE ${this.services.compileExpression(ast.where, ctx)}`
32
+ : '';
33
+ const returning = this.output.compileOutput(
34
+ ast.returning,
35
+ 'deleted',
36
+ id => this.services.quoteIdentifier(id)
37
+ );
38
+ return `DELETE ${this.services.quoteIdentifier(alias)}${returning} FROM ${target}${joins}${where}`;
39
+ }
40
+ }