metal-orm 1.0.42 → 1.0.44

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 (122) hide show
  1. package/README.md +195 -37
  2. package/dist/index.cjs +1014 -538
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +1267 -371
  5. package/dist/index.d.ts +1267 -371
  6. package/dist/index.js +1012 -536
  7. package/dist/index.js.map +1 -1
  8. package/package.json +8 -2
  9. package/scripts/run-eslint.mjs +34 -0
  10. package/src/codegen/typescript.ts +32 -15
  11. package/src/core/ast/adapters.ts +8 -2
  12. package/src/core/ast/builders.ts +105 -76
  13. package/src/core/ast/expression-builders.ts +430 -392
  14. package/src/core/ast/expression-nodes.ts +14 -5
  15. package/src/core/ast/expression-visitor.ts +56 -14
  16. package/src/core/ast/helpers.ts +23 -0
  17. package/src/core/ast/join-node.ts +18 -2
  18. package/src/core/ast/query.ts +6 -6
  19. package/src/core/ast/window-functions.ts +10 -2
  20. package/src/core/ddl/dialects/base-schema-dialect.ts +37 -4
  21. package/src/core/ddl/dialects/index.ts +1 -0
  22. package/src/core/ddl/dialects/mssql-schema-dialect.ts +5 -0
  23. package/src/core/ddl/dialects/mysql-schema-dialect.ts +3 -0
  24. package/src/core/ddl/dialects/postgres-schema-dialect.ts +14 -1
  25. package/src/core/ddl/dialects/render-reference.test.ts +69 -0
  26. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +10 -0
  27. package/src/core/ddl/introspect/catalogs/index.ts +1 -0
  28. package/src/core/ddl/introspect/catalogs/postgres.ts +2 -0
  29. package/src/core/ddl/introspect/context.ts +6 -0
  30. package/src/core/ddl/introspect/functions/postgres.ts +13 -0
  31. package/src/core/ddl/introspect/mssql.ts +53 -8
  32. package/src/core/ddl/introspect/mysql.ts +32 -6
  33. package/src/core/ddl/introspect/postgres.ts +102 -34
  34. package/src/core/ddl/introspect/registry.ts +14 -0
  35. package/src/core/ddl/introspect/run-select.ts +19 -4
  36. package/src/core/ddl/introspect/sqlite.ts +78 -11
  37. package/src/core/ddl/introspect/types.ts +0 -1
  38. package/src/core/ddl/introspect/utils.ts +21 -3
  39. package/src/core/ddl/naming-strategy.ts +6 -0
  40. package/src/core/ddl/schema-dialect.ts +20 -6
  41. package/src/core/ddl/schema-diff.ts +22 -0
  42. package/src/core/ddl/schema-generator.ts +26 -12
  43. package/src/core/ddl/schema-plan-executor.ts +6 -0
  44. package/src/core/ddl/schema-types.ts +6 -0
  45. package/src/core/ddl/sql-writing.ts +4 -4
  46. package/src/core/dialect/abstract.ts +19 -7
  47. package/src/core/dialect/base/function-table-formatter.ts +3 -2
  48. package/src/core/dialect/base/join-compiler.ts +5 -3
  49. package/src/core/dialect/base/returning-strategy.ts +1 -0
  50. package/src/core/dialect/base/sql-dialect.ts +3 -3
  51. package/src/core/dialect/mssql/functions.ts +24 -25
  52. package/src/core/dialect/mssql/index.ts +1 -4
  53. package/src/core/dialect/mysql/functions.ts +0 -1
  54. package/src/core/dialect/postgres/functions.ts +33 -34
  55. package/src/core/dialect/postgres/index.ts +1 -0
  56. package/src/core/dialect/sqlite/functions.ts +18 -19
  57. package/src/core/dialect/sqlite/index.ts +2 -0
  58. package/src/core/execution/db-executor.ts +1 -1
  59. package/src/core/execution/executors/mysql-executor.ts +2 -2
  60. package/src/core/execution/executors/postgres-executor.ts +1 -1
  61. package/src/core/execution/pooling/pool.ts +12 -5
  62. package/src/core/functions/datetime.ts +58 -34
  63. package/src/core/functions/numeric.ts +96 -31
  64. package/src/core/functions/standard-strategy.ts +35 -0
  65. package/src/core/functions/text.ts +84 -23
  66. package/src/core/functions/types.ts +23 -8
  67. package/src/decorators/bootstrap.ts +42 -11
  68. package/src/decorators/column.ts +20 -11
  69. package/src/decorators/decorator-metadata.ts +30 -9
  70. package/src/decorators/entity.ts +29 -5
  71. package/src/decorators/index.ts +3 -0
  72. package/src/decorators/relations.ts +34 -11
  73. package/src/orm/als.ts +34 -9
  74. package/src/orm/entity-context.ts +62 -8
  75. package/src/orm/entity-meta.ts +8 -8
  76. package/src/orm/entity-metadata.ts +131 -16
  77. package/src/orm/entity.ts +28 -29
  78. package/src/orm/execute.ts +19 -4
  79. package/src/orm/hydration.ts +42 -39
  80. package/src/orm/identity-map.ts +1 -1
  81. package/src/orm/lazy-batch.ts +74 -104
  82. package/src/orm/orm-session.ts +24 -23
  83. package/src/orm/orm.ts +2 -5
  84. package/src/orm/relation-change-processor.ts +12 -11
  85. package/src/orm/relations/belongs-to.ts +11 -11
  86. package/src/orm/relations/has-many.ts +54 -10
  87. package/src/orm/relations/has-one.ts +8 -7
  88. package/src/orm/relations/many-to-many.ts +13 -13
  89. package/src/orm/runtime-types.ts +4 -4
  90. package/src/orm/save-graph.ts +31 -25
  91. package/src/orm/unit-of-work.ts +17 -17
  92. package/src/query/index.ts +74 -0
  93. package/src/query/target.ts +46 -0
  94. package/src/query-builder/delete-query-state.ts +30 -0
  95. package/src/query-builder/delete.ts +64 -18
  96. package/src/query-builder/hydration-manager.ts +52 -5
  97. package/src/query-builder/insert-query-state.ts +30 -0
  98. package/src/query-builder/insert.ts +58 -10
  99. package/src/query-builder/query-ast-service.ts +7 -2
  100. package/src/query-builder/query-resolution.ts +78 -0
  101. package/src/query-builder/raw-column-parser.ts +7 -1
  102. package/src/query-builder/relation-alias.ts +7 -0
  103. package/src/query-builder/relation-conditions.ts +61 -48
  104. package/src/query-builder/relation-service.ts +68 -63
  105. package/src/query-builder/relation-utils.ts +3 -0
  106. package/src/query-builder/select/cte-facet.ts +40 -0
  107. package/src/query-builder/select/from-facet.ts +80 -0
  108. package/src/query-builder/select/join-facet.ts +62 -0
  109. package/src/query-builder/select/predicate-facet.ts +103 -0
  110. package/src/query-builder/select/projection-facet.ts +69 -0
  111. package/src/query-builder/select/relation-facet.ts +81 -0
  112. package/src/query-builder/select/setop-facet.ts +36 -0
  113. package/src/query-builder/select-helpers.ts +15 -2
  114. package/src/query-builder/select-query-builder-deps.ts +19 -1
  115. package/src/query-builder/select-query-state.ts +2 -1
  116. package/src/query-builder/select.ts +795 -1163
  117. package/src/query-builder/update-query-state.ts +52 -0
  118. package/src/query-builder/update.ts +69 -18
  119. package/src/schema/column.ts +26 -26
  120. package/src/schema/table-guards.ts +31 -0
  121. package/src/schema/table.ts +47 -18
  122. package/src/schema/types.ts +22 -22
@@ -146,11 +146,20 @@ const operandTypes = new Set<OperandNode['type']>([
146
146
  'WindowFunction'
147
147
  ]);
148
148
 
149
- export const isOperandNode = (node: any): node is OperandNode => node && operandTypes.has(node.type);
150
-
151
- export const isFunctionNode = (node: any): node is FunctionNode => node?.type === 'Function';
152
- export const isCaseExpressionNode = (node: any): node is CaseExpressionNode => node?.type === 'CaseExpression';
153
- export const isWindowFunctionNode = (node: any): node is WindowFunctionNode => node?.type === 'WindowFunction';
149
+ const hasTypeProperty = (value: unknown): value is { type?: string } =>
150
+ typeof value === 'object' && value !== null && 'type' in value;
151
+
152
+ export const isOperandNode = (node: unknown): node is OperandNode => {
153
+ if (!hasTypeProperty(node)) return false;
154
+ return operandTypes.has(node.type as OperandNode['type']);
155
+ };
156
+
157
+ export const isFunctionNode = (node: unknown): node is FunctionNode =>
158
+ isOperandNode(node) && node.type === 'Function';
159
+ export const isCaseExpressionNode = (node: unknown): node is CaseExpressionNode =>
160
+ isOperandNode(node) && node.type === 'CaseExpression';
161
+ export const isWindowFunctionNode = (node: unknown): node is WindowFunctionNode =>
162
+ isOperandNode(node) && node.type === 'WindowFunction';
154
163
  export const isExpressionSelectionNode = (
155
164
  node: ColumnRef | FunctionNode | CaseExpressionNode | WindowFunctionNode
156
165
  ): node is FunctionNode | CaseExpressionNode | WindowFunctionNode =>
@@ -47,18 +47,52 @@ export interface OperandVisitor<R> {
47
47
  otherwise?(node: OperandNode): R;
48
48
  }
49
49
 
50
- type ExpressionDispatch = <R>(node: any, visitor: ExpressionVisitor<R>) => R;
51
- type OperandDispatch = <R>(node: any, visitor: OperandVisitor<R>) => R;
50
+ type ExpressionDispatch = <R>(node: ExpressionNode, visitor: ExpressionVisitor<R>) => R;
51
+ type OperandDispatch = <R>(node: OperandNode, visitor: OperandVisitor<R>) => R;
52
52
 
53
- const expressionDispatchers = new Map<string, ExpressionDispatch>();
54
- const operandDispatchers = new Map<string, OperandDispatch>();
53
+ /**
54
+ * Registry class for managing dispatchers in an immutable way
55
+ */
56
+ class DispatcherRegistry<T> {
57
+ private readonly dispatchers: ReadonlyMap<string, T>;
58
+
59
+ constructor(dispatchers: Map<string, T> = new Map()) {
60
+ this.dispatchers = dispatchers;
61
+ }
62
+
63
+ /**
64
+ * Registers a new dispatcher and returns a new registry instance
65
+ */
66
+ register(type: string, dispatcher: T): DispatcherRegistry<T> {
67
+ const newMap = new Map(this.dispatchers);
68
+ newMap.set(type, dispatcher);
69
+ return new DispatcherRegistry(newMap);
70
+ }
71
+
72
+ /**
73
+ * Gets a dispatcher for the given type
74
+ */
75
+ get(type: string): T | undefined {
76
+ return this.dispatchers.get(type);
77
+ }
78
+
79
+ /**
80
+ * Returns a new empty registry
81
+ */
82
+ clear(): DispatcherRegistry<T> {
83
+ return new DispatcherRegistry();
84
+ }
85
+ }
86
+
87
+ let expressionRegistry = new DispatcherRegistry<ExpressionDispatch>();
88
+ let operandRegistry = new DispatcherRegistry<OperandDispatch>();
55
89
 
56
90
  /**
57
91
  * Registers a dispatcher for a custom expression node type.
58
92
  * Allows new node kinds without modifying the core switch.
59
93
  */
60
94
  export const registerExpressionDispatcher = (type: string, dispatcher: ExpressionDispatch): void => {
61
- expressionDispatchers.set(type, dispatcher);
95
+ expressionRegistry = expressionRegistry.register(type, dispatcher);
62
96
  };
63
97
 
64
98
  /**
@@ -66,21 +100,29 @@ export const registerExpressionDispatcher = (type: string, dispatcher: Expressio
66
100
  * Allows new node kinds without modifying the core switch.
67
101
  */
68
102
  export const registerOperandDispatcher = (type: string, dispatcher: OperandDispatch): void => {
69
- operandDispatchers.set(type, dispatcher);
103
+ operandRegistry = operandRegistry.register(type, dispatcher);
70
104
  };
71
105
 
72
106
  /**
73
107
  * Clears all registered dispatchers. Primarily for tests.
74
108
  */
75
- export const clearExpressionDispatchers = (): void => expressionDispatchers.clear();
76
- export const clearOperandDispatchers = (): void => operandDispatchers.clear();
109
+ export const clearExpressionDispatchers = (): void => {
110
+ expressionRegistry = expressionRegistry.clear();
111
+ };
112
+
113
+ export const clearOperandDispatchers = (): void => {
114
+ operandRegistry = operandRegistry.clear();
115
+ };
116
+
117
+ const getNodeType = (node: { type?: string } | null | undefined): string | undefined =>
118
+ typeof node === 'object' && node !== null && typeof node.type === 'string' ? node.type : undefined;
77
119
 
78
120
  const unsupportedExpression = (node: ExpressionNode): never => {
79
- throw new Error(`Unsupported expression type "${(node as any)?.type ?? 'unknown'}"`);
121
+ throw new Error(`Unsupported expression type "${getNodeType(node) ?? 'unknown'}"`);
80
122
  };
81
123
 
82
124
  const unsupportedOperand = (node: OperandNode): never => {
83
- throw new Error(`Unsupported operand type "${(node as any)?.type ?? 'unknown'}"`);
125
+ throw new Error(`Unsupported operand type "${getNodeType(node) ?? 'unknown'}"`);
84
126
  };
85
127
  /**
86
128
  * Dispatches an expression node to the visitor
@@ -88,8 +130,8 @@ const unsupportedOperand = (node: OperandNode): never => {
88
130
  * @param visitor - Visitor implementation
89
131
  */
90
132
  export const visitExpression = <R>(node: ExpressionNode, visitor: ExpressionVisitor<R>): R => {
91
- const dynamic = expressionDispatchers.get((node as any)?.type);
92
- if (dynamic) return dynamic(node as any, visitor);
133
+ const dynamic = expressionRegistry.get(node.type);
134
+ if (dynamic) return dynamic(node, visitor);
93
135
 
94
136
  switch (node.type) {
95
137
  case 'BinaryExpression':
@@ -126,8 +168,8 @@ export const visitExpression = <R>(node: ExpressionNode, visitor: ExpressionVisi
126
168
  * @param visitor - Visitor implementation
127
169
  */
128
170
  export const visitOperand = <R>(node: OperandNode, visitor: OperandVisitor<R>): R => {
129
- const dynamic = operandDispatchers.get((node as any)?.type);
130
- if (dynamic) return dynamic(node as any, visitor);
171
+ const dynamic = operandRegistry.get(node.type);
172
+ if (dynamic) return dynamic(node, visitor);
131
173
 
132
174
  switch (node.type) {
133
175
  case 'Column':
@@ -0,0 +1,23 @@
1
+ import { ColumnNode, LiteralNode } from './expression.js';
2
+
3
+ /**
4
+ * Creates a column node for use in expressions
5
+ * @param table - Table name
6
+ * @param name - Column name
7
+ * @returns ColumnNode with the specified table and name
8
+ */
9
+ export const createColumn = (table: string, name: string): ColumnNode => ({
10
+ type: 'Column',
11
+ table,
12
+ name
13
+ });
14
+
15
+ /**
16
+ * Creates a literal value node for use in expressions
17
+ * @param val - Literal value (string or number)
18
+ * @returns LiteralNode with the specified value
19
+ */
20
+ export const createLiteral = (val: string | number): LiteralNode => ({
21
+ type: 'Literal',
22
+ value: val
23
+ });
@@ -2,7 +2,7 @@ import { JoinNode } from './join.js';
2
2
  import { ExpressionNode } from './expression.js';
3
3
  import { JoinKind } from '../sql/sql.js';
4
4
  import { JoinMetadata } from './join-metadata.js';
5
- import { TableSourceNode, TableNode, FunctionTableNode } from './query.js';
5
+ import { TableSourceNode, TableNode } from './query.js';
6
6
 
7
7
  /**
8
8
  * Creates a JoinNode ready for AST insertion.
@@ -17,8 +17,24 @@ export const createJoinNode = (
17
17
  type: 'Join',
18
18
  kind,
19
19
  table: typeof tableName === 'string'
20
- ? ({ type: 'Table', name: tableName } as TableNode)
20
+ ? (parseQualifiedTableRef(tableName) as TableNode)
21
21
  : (tableName as TableSourceNode),
22
22
  condition,
23
23
  meta: relationName ? ({ relationName } as JoinMetadata) : undefined
24
24
  });
25
+
26
+ /**
27
+ * Parses a simple qualified reference like `schema.table` into a structured TableNode.
28
+ *
29
+ * Notes:
30
+ * - We intentionally only support a single dot here.
31
+ * - For multi-part qualification (server/db/schema/table), callers should pass a TableNode.
32
+ */
33
+ const parseQualifiedTableRef = (ref: string): TableNode => {
34
+ const parts = ref.split('.');
35
+ if (parts.length === 2) {
36
+ const [schema, name] = parts;
37
+ return { type: 'Table', schema, name };
38
+ }
39
+ return { type: 'Table', name: ref };
40
+ };
@@ -1,12 +1,12 @@
1
1
  import {
2
+ AliasRefNode,
3
+ CaseExpressionNode,
2
4
  ColumnNode,
3
- FunctionNode,
4
5
  ExpressionNode,
5
- ScalarSubqueryNode,
6
- CaseExpressionNode,
7
- WindowFunctionNode,
6
+ FunctionNode,
8
7
  OperandNode,
9
- AliasRefNode
8
+ ScalarSubqueryNode,
9
+ WindowFunctionNode
10
10
  } from './expression.js';
11
11
  import { JoinNode } from './join.js';
12
12
  import { OrderDirection } from '../sql/sql.js';
@@ -34,7 +34,7 @@ export interface FunctionTableNode {
34
34
  /** Optional schema for the function (some dialects) */
35
35
  schema?: string;
36
36
  /** Function arguments as operand nodes */
37
- args?: any[]; // use any to avoid circular import here; caller should supply OperandNode
37
+ args?: OperandNode[];
38
38
  /** Optional alias for the function table */
39
39
  alias?: string;
40
40
  /** LATERAL flag */
@@ -60,7 +60,11 @@ export const ntile = (n: number): WindowFunctionNode =>
60
60
  * @param defaultValue - Default value if no row exists
61
61
  * @returns Window function node for LAG
62
62
  */
63
- export const lag = (col: ColumnRef | ColumnNode, offset: number = 1, defaultValue?: any): WindowFunctionNode => {
63
+ export const lag = (
64
+ col: ColumnRef | ColumnNode,
65
+ offset: number = 1,
66
+ defaultValue?: LiteralNode['value']
67
+ ): WindowFunctionNode => {
64
68
  const args: (ColumnNode | LiteralNode | JsonPathNode)[] = [
65
69
  columnOperand(col),
66
70
  { type: 'Literal', value: offset }
@@ -78,7 +82,11 @@ export const lag = (col: ColumnRef | ColumnNode, offset: number = 1, defaultValu
78
82
  * @param defaultValue - Default value if no row exists
79
83
  * @returns Window function node for LEAD
80
84
  */
81
- export const lead = (col: ColumnRef | ColumnNode, offset: number = 1, defaultValue?: any): WindowFunctionNode => {
85
+ export const lead = (
86
+ col: ColumnRef | ColumnNode,
87
+ offset: number = 1,
88
+ defaultValue?: LiteralNode['value']
89
+ ): WindowFunctionNode => {
82
90
  const args: (ColumnNode | LiteralNode | JsonPathNode)[] = [
83
91
  columnOperand(col),
84
92
  { type: 'Literal', value: offset }
@@ -1,9 +1,10 @@
1
1
  import { SchemaDialect, DialectName } from '../schema-dialect.js';
2
- import { formatLiteral, quoteQualified, createLiteralFormatter, LiteralFormatter } from '../sql-writing.js';
2
+ import { formatLiteral, quoteQualified, LiteralFormatter } from '../sql-writing.js';
3
3
  import { ColumnDef, ForeignKeyReference } from '../../../schema/column.js';
4
4
  import { IndexDef, TableDef } from '../../../schema/table.js';
5
5
  import { DatabaseTable, DatabaseColumn, ColumnDiff } from '../schema-types.js';
6
6
 
7
+ /** A table-like object with name and optional schema. */
7
8
  type TableLike = { name: string; schema?: string };
8
9
 
9
10
  /**
@@ -11,10 +12,15 @@ type TableLike = { name: string; schema?: string };
11
12
  * Concrete dialects only override the small surface area instead of reimplementing everything.
12
13
  */
13
14
  export abstract class BaseSchemaDialect implements SchemaDialect {
15
+ /** The name of the dialect. */
14
16
  abstract readonly name: DialectName;
17
+ /** Quotes an identifier for use in SQL. */
15
18
  abstract quoteIdentifier(id: string): string;
19
+ /** Renders the column type for SQL. */
16
20
  abstract renderColumnType(column: ColumnDef): string;
21
+ /** Renders the auto-increment clause for SQL. */
17
22
  abstract renderAutoIncrement(column: ColumnDef, table: TableDef): string | undefined;
23
+ /** Renders an index for SQL. */
18
24
  abstract renderIndex(table: TableDef, index: IndexDef): string;
19
25
  supportsPartialIndexes(): boolean {
20
26
  return false;
@@ -25,20 +31,30 @@ export abstract class BaseSchemaDialect implements SchemaDialect {
25
31
  }
26
32
  return this.quoteIdentifier(table.name);
27
33
  }
28
- // Each dialect should provide its own formatter
34
+ /** The literal formatter for the dialect. */
29
35
  abstract get literalFormatter(): LiteralFormatter;
30
36
 
31
37
  renderDefault(value: unknown, _column: ColumnDef): string {
38
+ void _column;
32
39
  return formatLiteral(this.literalFormatter, value);
33
40
  }
34
41
  renderReference(ref: ForeignKeyReference, _table: TableDef): string {
42
+ void _table;
35
43
  const parts = ['REFERENCES', quoteQualified(this, ref.table), `(${this.quoteIdentifier(ref.column)})`];
36
44
  if (ref.onDelete) parts.push('ON DELETE', ref.onDelete);
37
45
  if (ref.onUpdate) parts.push('ON UPDATE', ref.onUpdate);
38
- if (ref.deferrable && this.name === 'postgres') parts.push('DEFERRABLE INITIALLY DEFERRED');
46
+ const suffix = this.renderReferenceSuffix(ref, _table);
47
+ if (suffix) parts.push(suffix);
39
48
  return parts.join(' ');
40
49
  }
50
+
51
+ protected renderReferenceSuffix(ref: ForeignKeyReference, _table: TableDef): string | undefined {
52
+ void ref;
53
+ void _table;
54
+ return undefined;
55
+ }
41
56
  renderTableOptions(_table: TableDef): string | undefined {
57
+ void _table;
42
58
  return undefined;
43
59
  }
44
60
  dropTableSql(table: DatabaseTable): string[] {
@@ -51,12 +67,29 @@ export abstract class BaseSchemaDialect implements SchemaDialect {
51
67
  return [`DROP INDEX ${this.quoteIdentifier(index)};`];
52
68
  }
53
69
  warnDropColumn(_table: DatabaseTable, _column: string): string | undefined {
70
+ void _table;
71
+ void _column;
54
72
  return undefined;
55
73
  }
56
- alterColumnSql?(table: TableDef, column: ColumnDef, actualColumn: DatabaseColumn, diff: ColumnDiff): string[] {
74
+ alterColumnSql?(_table: TableDef, _column: ColumnDef, _actualColumn: DatabaseColumn, _diff: ColumnDiff): string[] {
75
+ void _table;
76
+ void _column;
77
+ void _actualColumn;
78
+ void _diff;
57
79
  return [];
58
80
  }
59
81
  warnAlterColumn?(_table: TableDef, _column: ColumnDef, _actual: DatabaseColumn, _diff: ColumnDiff): string | undefined {
82
+ void _table;
83
+ void _column;
84
+ void _actual;
85
+ void _diff;
60
86
  return undefined;
61
87
  }
88
+
89
+ preferInlinePkAutoincrement(_column: ColumnDef, _table: TableDef, _pk: string[]): boolean {
90
+ void _column;
91
+ void _table;
92
+ void _pk;
93
+ return false;
94
+ }
62
95
  }
@@ -1,3 +1,4 @@
1
+ /** Re-exports for schema dialects. */
1
2
  export { BaseSchemaDialect } from './base-schema-dialect.js';
2
3
  export { PostgresSchemaDialect } from './postgres-schema-dialect.js';
3
4
  export { MySqlSchemaDialect } from './mysql-schema-dialect.js';
@@ -6,6 +6,7 @@ import { IndexDef, TableDef } from '../../../schema/table.js';
6
6
  import { ColumnDiff, DatabaseColumn, DatabaseTable } from '../schema-types.js';
7
7
  import { DialectName } from '../schema-dialect.js';
8
8
 
9
+ /** MSSQL schema dialect implementation. */
9
10
  export class MSSqlSchemaDialect extends BaseSchemaDialect {
10
11
  name: DialectName = 'mssql';
11
12
 
@@ -120,6 +121,7 @@ export class MSSqlSchemaDialect extends BaseSchemaDialect {
120
121
  }
121
122
 
122
123
  alterColumnSql(table: TableDef, column: ColumnDef, _actual: DatabaseColumn, diff: ColumnDiff): string[] {
124
+ void _actual;
123
125
  const stmts: string[] = [];
124
126
  if (diff.typeChanged || diff.nullabilityChanged) {
125
127
  const nullability = column.notNull ? 'NOT NULL' : 'NULL';
@@ -131,6 +133,9 @@ export class MSSqlSchemaDialect extends BaseSchemaDialect {
131
133
  }
132
134
 
133
135
  warnAlterColumn(_table: TableDef, _column: ColumnDef, _actual: DatabaseColumn, diff: ColumnDiff): string | undefined {
136
+ void _table;
137
+ void _column;
138
+ void _actual;
134
139
  if (diff.defaultChanged || diff.autoIncrementChanged) {
135
140
  return 'Altering defaults or identity on MSSQL is not automated (requires dropping/adding default or identity constraints manually).';
136
141
  }
@@ -7,6 +7,7 @@ import { ColumnDiff, DatabaseColumn, DatabaseTable } from '../schema-types.js';
7
7
  import { renderColumnDefinition } from '../schema-generator.js';
8
8
  import { DialectName } from '../schema-dialect.js';
9
9
 
10
+ /** MySQL schema dialect implementation. */
10
11
  export class MySqlSchemaDialect extends BaseSchemaDialect {
11
12
  name: DialectName = 'mysql';
12
13
 
@@ -125,6 +126,8 @@ export class MySqlSchemaDialect extends BaseSchemaDialect {
125
126
  }
126
127
 
127
128
  alterColumnSql(table: TableDef, column: ColumnDef, _actual: DatabaseColumn, _diff: ColumnDiff): string[] {
129
+ void _actual;
130
+ void _diff;
128
131
  const rendered = renderColumnDefinition(table, column, this);
129
132
  return [`ALTER TABLE ${this.formatTableName(table)} MODIFY COLUMN ${rendered.sql};`];
130
133
  }
@@ -1,11 +1,12 @@
1
1
  import { BaseSchemaDialect } from './base-schema-dialect.js';
2
2
  import { deriveIndexName } from '../naming-strategy.js';
3
3
  import { renderIndexColumns, createLiteralFormatter } from '../sql-writing.js';
4
- import { ColumnDef } from '../../../schema/column.js';
4
+ import { ColumnDef, ForeignKeyReference } from '../../../schema/column.js';
5
5
  import { IndexDef, TableDef } from '../../../schema/table.js';
6
6
  import { ColumnDiff, DatabaseColumn, DatabaseTable } from '../schema-types.js';
7
7
  import { DialectName } from '../schema-dialect.js';
8
8
 
9
+ /** PostgreSQL schema dialect implementation. */
9
10
  export class PostgresSchemaDialect extends BaseSchemaDialect {
10
11
  readonly name: DialectName = 'postgres';
11
12
 
@@ -102,6 +103,14 @@ export class PostgresSchemaDialect extends BaseSchemaDialect {
102
103
  return true;
103
104
  }
104
105
 
106
+ protected renderReferenceSuffix(ref: ForeignKeyReference, _table: TableDef): string | undefined {
107
+ void _table;
108
+ if (ref.deferrable) {
109
+ return 'DEFERRABLE INITIALLY DEFERRED';
110
+ }
111
+ return undefined;
112
+ }
113
+
105
114
  dropColumnSql(table: DatabaseTable, column: string): string[] {
106
115
  return [`ALTER TABLE ${this.formatTableName(table)} DROP COLUMN ${this.quoteIdentifier(column)};`];
107
116
  }
@@ -114,6 +123,7 @@ export class PostgresSchemaDialect extends BaseSchemaDialect {
114
123
  }
115
124
 
116
125
  alterColumnSql(table: TableDef, column: ColumnDef, actualColumn: DatabaseColumn, diff: ColumnDiff): string[] {
126
+ void actualColumn;
117
127
  const stmts: string[] = [];
118
128
  const tableName = this.formatTableName(table);
119
129
  const colName = this.quoteIdentifier(column.name);
@@ -145,6 +155,9 @@ export class PostgresSchemaDialect extends BaseSchemaDialect {
145
155
  }
146
156
 
147
157
  warnAlterColumn(_table: TableDef, _column: ColumnDef, _actual: DatabaseColumn, diff: ColumnDiff): string | undefined {
158
+ void _table;
159
+ void _column;
160
+ void _actual;
148
161
  if (diff.autoIncrementChanged) {
149
162
  return 'Altering identity properties may fail if an existing sequence is attached; verify generated column state.';
150
163
  }
@@ -0,0 +1,69 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { BaseSchemaDialect } from './base-schema-dialect.js';
3
+ import { PostgresSchemaDialect } from './postgres-schema-dialect.js';
4
+ import { TableDef } from '../../../schema/table.js';
5
+ import { ForeignKeyReference } from '../../../schema/column.js';
6
+ import { createLiteralFormatter } from '../sql-writing.js';
7
+
8
+ class DummySchemaDialect extends BaseSchemaDialect {
9
+ readonly name = 'sqlite';
10
+ private readonly formatter = createLiteralFormatter({
11
+ booleanTrue: '1',
12
+ booleanFalse: '0',
13
+ });
14
+
15
+ get literalFormatter() {
16
+ return this.formatter;
17
+ }
18
+
19
+ quoteIdentifier(id: string): string {
20
+ return `"${id}"`;
21
+ }
22
+
23
+ renderColumnType(): string {
24
+ return 'INTEGER';
25
+ }
26
+
27
+ renderAutoIncrement(): string | undefined {
28
+ return undefined;
29
+ }
30
+
31
+ renderIndex(): string {
32
+ return 'CREATE INDEX dummy;';
33
+ }
34
+ }
35
+
36
+ const table: TableDef = {
37
+ name: 'child',
38
+ columns: {},
39
+ relations: {},
40
+ };
41
+
42
+ const deferrableReference: ForeignKeyReference = {
43
+ table: 'parent',
44
+ column: 'id',
45
+ deferrable: true,
46
+ onDelete: 'CASCADE',
47
+ onUpdate: 'NO ACTION',
48
+ };
49
+
50
+ describe('renderReference deferrable handling', () => {
51
+ it('base dialect remains agnostic to deferrable flags', () => {
52
+ const dialect = new DummySchemaDialect();
53
+ const sql = dialect.renderReference(deferrableReference, table);
54
+ expect(sql).toContain('REFERENCES "parent"');
55
+ expect(sql).not.toContain('DEFERRABLE INITIALLY DEFERRED');
56
+ });
57
+
58
+ it('Postgres dialect renders the deferrable clause', () => {
59
+ const dialect = new PostgresSchemaDialect();
60
+ const sql = dialect.renderReference(deferrableReference, table);
61
+ expect(sql).toContain('DEFERRABLE INITIALLY DEFERRED');
62
+ });
63
+
64
+ it('Postgres dialect skips the clause when the flag is missing', () => {
65
+ const dialect = new PostgresSchemaDialect();
66
+ const sql = dialect.renderReference({ table: 'parent', column: 'id' }, table);
67
+ expect(sql).not.toContain('DEFERRABLE INITIALLY DEFERRED');
68
+ });
69
+ });
@@ -6,6 +6,7 @@ import { IndexDef, TableDef } from '../../../schema/table.js';
6
6
  import { ColumnDiff, DatabaseColumn, DatabaseTable } from '../schema-types.js';
7
7
  import { DialectName } from '../schema-dialect.js';
8
8
 
9
+ /** SQLite schema dialect implementation. */
9
10
  export class SQLiteSchemaDialect extends BaseSchemaDialect {
10
11
  name: DialectName = 'sqlite';
11
12
 
@@ -103,10 +104,13 @@ export class SQLiteSchemaDialect extends BaseSchemaDialect {
103
104
  }
104
105
 
105
106
  dropColumnSql(_table: DatabaseTable, _column: string): string[] {
107
+ void _table;
108
+ void _column;
106
109
  return [];
107
110
  }
108
111
 
109
112
  dropIndexSql(_table: DatabaseTable, index: string): string[] {
113
+ void _table;
110
114
  return [`DROP INDEX IF EXISTS ${this.quoteIdentifier(index)};`];
111
115
  }
112
116
 
@@ -116,10 +120,16 @@ export class SQLiteSchemaDialect extends BaseSchemaDialect {
116
120
  }
117
121
 
118
122
  alterColumnSql(_table: TableDef, _column: ColumnDef, _actual: DatabaseColumn, _diff: ColumnDiff): string[] {
123
+ void _table;
124
+ void _column;
125
+ void _actual;
126
+ void _diff;
119
127
  return [];
120
128
  }
121
129
 
122
130
  warnAlterColumn(table: TableDef, column: ColumnDef, _actual: DatabaseColumn, _diff: ColumnDiff): string | undefined {
131
+ void _actual;
132
+ void _diff;
123
133
  const key = table.schema ? `${table.schema}.${table.name}` : table.name;
124
134
  return `SQLite ALTER COLUMN is not supported; rebuild table ${key} to change column ${column.name}.`;
125
135
  }
@@ -1 +1,2 @@
1
+ /** Re-exports for PostgreSQL catalog definitions. */
1
2
  export * from './postgres.js';
@@ -1,6 +1,7 @@
1
1
  import { defineTable } from '../../../../schema/table.js';
2
2
  import { col } from '../../../../schema/column.js';
3
3
 
4
+ /** Table definition for information_schema.columns, providing metadata about table columns. */
4
5
  export const PgInformationSchemaColumns = defineTable(
5
6
  'columns',
6
7
  {
@@ -134,6 +135,7 @@ export const PgReferentialConstraints = defineTable(
134
135
  { schema: 'information_schema' }
135
136
  );
136
137
 
138
+ /** Default export containing commonly used PostgreSQL catalog table definitions. */
137
139
  export default {
138
140
  PgInformationSchemaColumns,
139
141
  PgClass,
@@ -1,8 +1,14 @@
1
1
  import type { Dialect } from '../../dialect/abstract.js';
2
2
  import type { DbExecutor } from '../../execution/db-executor.js';
3
3
 
4
+ /**
5
+ * Context for schema introspection operations.
6
+ * Provides the necessary components to perform database schema introspection.
7
+ */
4
8
  export interface IntrospectContext {
9
+ /** The database dialect used for introspection. */
5
10
  dialect: Dialect;
11
+ /** The database executor for running introspection queries. */
6
12
  executor: DbExecutor;
7
13
  }
8
14
 
@@ -13,10 +13,23 @@ const fn = (name: string, args: OperandInput[]): FunctionNode => ({
13
13
  args: args.map(toOperand)
14
14
  });
15
15
 
16
+ /**
17
+ * Creates a pg_get_expr function call AST node.
18
+ * @param expr - The expression.
19
+ * @param relid - The relation ID.
20
+ * @returns The function node.
21
+ */
16
22
  export const pgGetExpr = (expr: OperandInput, relid: OperandInput): FunctionNode =>
17
23
  fn('pg_get_expr', [expr, relid]);
18
24
 
25
+ /**
26
+ * Creates a format_type function call AST node.
27
+ * @param typeOid - The type OID.
28
+ * @param typmod - The type modifier.
29
+ * @returns The function node.
30
+ */
19
31
  export const formatType = (typeOid: OperandInput, typmod: OperandInput): FunctionNode =>
20
32
  fn('format_type', [typeOid, typmod]);
21
33
 
34
+ /** Default export with PostgreSQL-specific function helpers. */
22
35
  export default { pgGetExpr, formatType };