metal-orm 1.0.46 → 1.0.47

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metal-orm",
3
- "version": "1.0.46",
3
+ "version": "1.0.47",
4
4
  "type": "module",
5
5
  "types": "./dist/index.d.ts",
6
6
  "engines": {
@@ -267,6 +267,7 @@ const renderColumnExpression = (column, tablePk) => {
267
267
  };
268
268
 
269
269
  const mapRelations = tables => {
270
+ const normalizeName = name => (typeof name === 'string' && name.includes('.') ? name.split('.').pop() : name);
270
271
  const relationMap = new Map();
271
272
  const relationKeys = new Map();
272
273
  const fkIndex = new Map();
@@ -283,12 +284,15 @@ const mapRelations = tables => {
283
284
  }
284
285
  }
285
286
 
286
- const findTable = name => tables.find(t => t.name === name);
287
+ const findTable = name => {
288
+ const norm = normalizeName(name);
289
+ return tables.find(t => t.name === name || t.name === norm);
290
+ };
287
291
 
288
292
  const pivotTables = new Set();
289
293
  for (const table of tables) {
290
294
  const fkCols = fkIndex.get(table.name) || [];
291
- const distinctTargets = Array.from(new Set(fkCols.map(c => c.references.table)));
295
+ const distinctTargets = Array.from(new Set(fkCols.map(c => normalizeName(c.references.table))));
292
296
  if (fkCols.length === 2 && distinctTargets.length === 2) {
293
297
  const [a, b] = fkCols;
294
298
  pivotTables.add(table.name);
@@ -329,8 +333,11 @@ const mapRelations = tables => {
329
333
  const fkCols = fkIndex.get(table.name) || [];
330
334
  for (const fk of fkCols) {
331
335
  const targetTable = fk.references.table;
336
+ const targetKey = normalizeName(targetTable);
332
337
  const belongsKey = relationKeys.get(table.name);
333
- const hasManyKey = relationKeys.get(targetTable);
338
+ const hasManyKey = targetKey ? relationKeys.get(targetKey) : undefined;
339
+
340
+ if (!belongsKey || !hasManyKey) continue;
334
341
 
335
342
  const belongsProp = deriveBelongsToName(fk.name, targetTable);
336
343
  if (!belongsKey.has(belongsProp)) {
@@ -346,7 +353,7 @@ const mapRelations = tables => {
346
353
  const hasManyProp = deriveHasManyName(table.name);
347
354
  if (!hasManyKey.has(hasManyProp)) {
348
355
  hasManyKey.add(hasManyProp);
349
- relationMap.get(targetTable)?.push({
356
+ relationMap.get(targetKey)?.push({
350
357
  kind: 'hasMany',
351
358
  property: hasManyProp,
352
359
  target: table.name,
@@ -362,12 +369,32 @@ const mapRelations = tables => {
362
369
  const renderEntityFile = (schema, options) => {
363
370
  const tables = schema.tables.map(t => ({
364
371
  name: t.name,
372
+ schema: t.schema,
365
373
  columns: t.columns,
366
374
  primaryKey: t.primaryKey || []
367
375
  }));
368
376
 
369
377
  const classNames = new Map();
370
- tables.forEach(t => classNames.set(t.name, deriveClassName(t.name)));
378
+ tables.forEach(t => {
379
+ const className = deriveClassName(t.name);
380
+ classNames.set(t.name, className);
381
+ if (t.schema) {
382
+ const qualified = `${t.schema}.${t.name}`;
383
+ if (!classNames.has(qualified)) {
384
+ classNames.set(qualified, className);
385
+ }
386
+ }
387
+ });
388
+
389
+ const resolveClassName = target => {
390
+ if (!target) return undefined;
391
+ if (classNames.has(target)) return classNames.get(target);
392
+ const fallback = target.split('.').pop();
393
+ if (fallback && classNames.has(fallback)) {
394
+ return classNames.get(fallback);
395
+ }
396
+ return undefined;
397
+ };
371
398
 
372
399
  const relations = mapRelations(tables);
373
400
 
@@ -469,7 +496,7 @@ const renderEntityFile = (schema, options) => {
469
496
 
470
497
  const rels = relations.get(table.name) || [];
471
498
  for (const rel of rels) {
472
- const targetClass = classNames.get(rel.target);
499
+ const targetClass = resolveClassName(rel.target);
473
500
  if (!targetClass) continue;
474
501
  switch (rel.kind) {
475
502
  case 'belongsTo':
@@ -487,10 +514,10 @@ const renderEntityFile = (schema, options) => {
487
514
  lines.push('');
488
515
  break;
489
516
  case 'belongsToMany':
517
+ const pivotClass = resolveClassName(rel.pivotTable);
518
+ if (!pivotClass) break;
490
519
  lines.push(
491
- ` @BelongsToMany({ target: () => ${targetClass}, pivotTable: () => ${classNames.get(
492
- rel.pivotTable
493
- )}, pivotForeignKeyToRoot: '${escapeJsString(rel.pivotForeignKeyToRoot)}', pivotForeignKeyToTarget: '${escapeJsString(rel.pivotForeignKeyToTarget)}' })`
520
+ ` @BelongsToMany({ target: () => ${targetClass}, pivotTable: () => ${pivotClass}, pivotForeignKeyToRoot: '${escapeJsString(rel.pivotForeignKeyToRoot)}', pivotForeignKeyToTarget: '${escapeJsString(rel.pivotForeignKeyToTarget)}' })`
494
521
  );
495
522
  lines.push(` ${rel.property}!: ManyToManyCollection<${targetClass}>;`);
496
523
  lines.push('');
@@ -17,6 +17,7 @@ import {
17
17
  LiteralNode,
18
18
  FunctionNode,
19
19
  AliasRefNode,
20
+ CastExpressionNode,
20
21
  ExpressionVisitor,
21
22
  OperandVisitor,
22
23
  visitExpression,
@@ -41,7 +42,8 @@ type SelectionColumn =
41
42
  | FunctionNode
42
43
  | ScalarSubqueryNode
43
44
  | CaseExpressionNode
44
- | WindowFunctionNode;
45
+ | WindowFunctionNode
46
+ | CastExpressionNode;
45
47
 
46
48
  export class TypeScriptGenerator implements ExpressionVisitor<string>, OperandVisitor<string> {
47
49
  constructor(private namingStrategy: NamingStrategy = new DefaultNamingStrategy()) { }
@@ -181,15 +183,16 @@ export class TypeScriptGenerator implements ExpressionVisitor<string>, OperandVi
181
183
  return `${this.namingStrategy.tableToSymbol(term.table)}.${term.name}`;
182
184
  case 'AliasRef':
183
185
  return this.visitAliasRef(term);
184
- case 'Literal':
185
- case 'Function':
186
- case 'JsonPath':
187
- case 'ScalarSubquery':
188
- case 'CaseExpression':
189
- case 'WindowFunction':
190
- return this.printOperand(term);
191
- default:
192
- return this.printExpression(term);
186
+ case 'Literal':
187
+ case 'Function':
188
+ case 'JsonPath':
189
+ case 'ScalarSubquery':
190
+ case 'CaseExpression':
191
+ case 'WindowFunction':
192
+ case 'Cast':
193
+ return this.printOperand(term);
194
+ default:
195
+ return this.printExpression(term);
193
196
  }
194
197
  }
195
198
 
@@ -263,6 +266,10 @@ export class TypeScriptGenerator implements ExpressionVisitor<string>, OperandVi
263
266
  return this.printWindowFunctionOperand(node);
264
267
  }
265
268
 
269
+ public visitCast(node: CastExpressionNode): string {
270
+ return this.printCastOperand(node);
271
+ }
272
+
266
273
  public visitAliasRef(node: AliasRefNode): string {
267
274
  return `aliasRef('${node.name}')`;
268
275
  }
@@ -454,6 +461,11 @@ export class TypeScriptGenerator implements ExpressionVisitor<string>, OperandVi
454
461
  return result;
455
462
  }
456
463
 
464
+ private printCastOperand(node: CastExpressionNode): string {
465
+ const typeLiteral = node.castType.replace(/'/g, "\\'");
466
+ return `cast(${this.printOperand(node.expression)}, '${typeLiteral}')`;
467
+ }
468
+
457
469
  /**
458
470
  * Converts method chain lines to inline format
459
471
  * @param lines - Method chain lines
@@ -7,6 +7,7 @@ import {
7
7
  JsonPathNode,
8
8
  OperandNode,
9
9
  CaseExpressionNode,
10
+ CastExpressionNode,
10
11
  BinaryExpressionNode,
11
12
  ExpressionNode,
12
13
  LogicalExpressionNode,
@@ -407,6 +408,18 @@ export const caseWhen = (
407
408
  else: elseValue !== undefined ? toOperand(elseValue) : undefined
408
409
  });
409
410
 
411
+ /**
412
+ * Builds a CAST expression node for casting values to SQL types.
413
+ */
414
+ export const cast = (
415
+ expression: OperandNode | ColumnRef | string | number | boolean | null,
416
+ castType: string
417
+ ): CastExpressionNode => ({
418
+ type: 'Cast',
419
+ expression: toOperand(expression),
420
+ castType
421
+ });
422
+
410
423
  /**
411
424
  * Creates an EXISTS expression
412
425
  * @param subquery - Subquery to check for existence
@@ -95,6 +95,19 @@ export interface CaseExpressionNode {
95
95
  alias?: string;
96
96
  }
97
97
 
98
+ /**
99
+ * AST node representing a CAST expression (CAST(value AS type)).
100
+ */
101
+ export interface CastExpressionNode {
102
+ type: 'Cast';
103
+ /** Expression being cast */
104
+ expression: OperandNode;
105
+ /** SQL type literal, e.g. "varchar(255)" */
106
+ castType: string;
107
+ /** Optional alias for the result */
108
+ alias?: string;
109
+ }
110
+
98
111
  /**
99
112
  * AST node representing a window function
100
113
  */
@@ -133,7 +146,9 @@ export type OperandNode =
133
146
  | JsonPathNode
134
147
  | ScalarSubqueryNode
135
148
  | CaseExpressionNode
136
- | WindowFunctionNode;
149
+ | CastExpressionNode
150
+ | WindowFunctionNode
151
+ | ArithmeticExpressionNode;
137
152
 
138
153
  const operandTypes = new Set<OperandNode['type']>([
139
154
  'AliasRef',
@@ -143,7 +158,9 @@ const operandTypes = new Set<OperandNode['type']>([
143
158
  'JsonPath',
144
159
  'ScalarSubquery',
145
160
  'CaseExpression',
146
- 'WindowFunction'
161
+ 'Cast',
162
+ 'WindowFunction',
163
+ 'ArithmeticExpression'
147
164
  ]);
148
165
 
149
166
  const hasTypeProperty = (value: unknown): value is { type?: string } =>
@@ -158,12 +175,15 @@ export const isFunctionNode = (node: unknown): node is FunctionNode =>
158
175
  isOperandNode(node) && node.type === 'Function';
159
176
  export const isCaseExpressionNode = (node: unknown): node is CaseExpressionNode =>
160
177
  isOperandNode(node) && node.type === 'CaseExpression';
178
+
179
+ export const isCastExpressionNode = (node: unknown): node is CastExpressionNode =>
180
+ isOperandNode(node) && node.type === 'Cast';
161
181
  export const isWindowFunctionNode = (node: unknown): node is WindowFunctionNode =>
162
182
  isOperandNode(node) && node.type === 'WindowFunction';
163
183
  export const isExpressionSelectionNode = (
164
- node: ColumnRef | FunctionNode | CaseExpressionNode | WindowFunctionNode
165
- ): node is FunctionNode | CaseExpressionNode | WindowFunctionNode =>
166
- isFunctionNode(node) || isCaseExpressionNode(node) || isWindowFunctionNode(node);
184
+ node: ColumnRef | FunctionNode | CaseExpressionNode | CastExpressionNode | WindowFunctionNode
185
+ ): node is FunctionNode | CaseExpressionNode | CastExpressionNode | WindowFunctionNode =>
186
+ isFunctionNode(node) || isCaseExpressionNode(node) || isCastExpressionNode(node) || isWindowFunctionNode(node);
167
187
 
168
188
  /**
169
189
  * AST node representing a binary expression (e.g., column = value)
@@ -14,6 +14,7 @@ import {
14
14
  JsonPathNode,
15
15
  ScalarSubqueryNode,
16
16
  CaseExpressionNode,
17
+ CastExpressionNode,
17
18
  WindowFunctionNode,
18
19
  AliasRefNode
19
20
  } from './expression-nodes.js';
@@ -42,6 +43,7 @@ export interface OperandVisitor<R> {
42
43
  visitJsonPath?(node: JsonPathNode): R;
43
44
  visitScalarSubquery?(node: ScalarSubqueryNode): R;
44
45
  visitCaseExpression?(node: CaseExpressionNode): R;
46
+ visitCast?(node: CastExpressionNode): R;
45
47
  visitWindowFunction?(node: WindowFunctionNode): R;
46
48
  visitAliasRef?(node: AliasRefNode): R;
47
49
  otherwise?(node: OperandNode): R;
@@ -196,6 +198,9 @@ export const visitOperand = <R>(node: OperandNode, visitor: OperandVisitor<R>):
196
198
  case 'AliasRef':
197
199
  if (visitor.visitAliasRef) return visitor.visitAliasRef(node);
198
200
  break;
201
+ case 'Cast':
202
+ if (visitor.visitCast) return visitor.visitCast(node);
203
+ break;
199
204
  default:
200
205
  break;
201
206
  }
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  AliasRefNode,
3
3
  CaseExpressionNode,
4
+ CastExpressionNode,
4
5
  ColumnNode,
5
6
  ExpressionNode,
6
7
  FunctionNode,
@@ -121,7 +122,14 @@ export interface SelectQueryNode {
121
122
  /** FROM clause table (either a Table or a FunctionTable) */
122
123
  from: TableSourceNode;
123
124
  /** SELECT clause columns */
124
- columns: (ColumnNode | FunctionNode | ScalarSubqueryNode | CaseExpressionNode | WindowFunctionNode)[];
125
+ columns: (
126
+ ColumnNode |
127
+ FunctionNode |
128
+ ScalarSubqueryNode |
129
+ CaseExpressionNode |
130
+ CastExpressionNode |
131
+ WindowFunctionNode
132
+ )[];
125
133
  /** JOIN clauses */
126
134
  joins: JoinNode[];
127
135
  /** Optional WHERE clause */
@@ -1,2 +1,5 @@
1
- /** Re-exports for PostgreSQL catalog definitions. */
1
+ /** Re-exports for supported catalog definitions. */
2
2
  export * from './postgres.js';
3
+ export * from './mssql.js';
4
+ export * from './mysql.js';
5
+ export * from './sqlite.js';
@@ -0,0 +1,126 @@
1
+ import { defineTable } from '../../../../schema/table.js';
2
+ import { col } from '../../../../schema/column-types.js';
3
+
4
+ /** Table definitions for SQL Server catalog views used during introspection. */
5
+ export const SysColumns = defineTable(
6
+ 'columns',
7
+ {
8
+ object_id: col.int(),
9
+ name: col.varchar(255),
10
+ column_id: col.int(),
11
+ max_length: col.int(),
12
+ precision: col.int(),
13
+ scale: col.int(),
14
+ is_nullable: col.boolean(),
15
+ is_identity: col.boolean(),
16
+ default_object_id: col.int(),
17
+ user_type_id: col.int()
18
+ },
19
+ {},
20
+ undefined,
21
+ { schema: 'sys' }
22
+ );
23
+
24
+ export const SysTables = defineTable(
25
+ 'tables',
26
+ {
27
+ object_id: col.int(),
28
+ name: col.varchar(255),
29
+ schema_id: col.int(),
30
+ is_ms_shipped: col.boolean()
31
+ },
32
+ {},
33
+ undefined,
34
+ { schema: 'sys' }
35
+ );
36
+
37
+ export const SysSchemas = defineTable(
38
+ 'schemas',
39
+ {
40
+ schema_id: col.int(),
41
+ name: col.varchar(255)
42
+ },
43
+ {},
44
+ undefined,
45
+ { schema: 'sys' }
46
+ );
47
+
48
+ export const SysTypes = defineTable(
49
+ 'types',
50
+ {
51
+ user_type_id: col.int(),
52
+ name: col.varchar(255)
53
+ },
54
+ {},
55
+ undefined,
56
+ { schema: 'sys' }
57
+ );
58
+
59
+ export const SysIndexes = defineTable(
60
+ 'indexes',
61
+ {
62
+ object_id: col.int(),
63
+ index_id: col.int(),
64
+ name: col.varchar(255),
65
+ is_primary_key: col.boolean(),
66
+ is_unique: col.boolean(),
67
+ has_filter: col.boolean(),
68
+ filter_definition: col.varchar(1024),
69
+ is_hypothetical: col.boolean()
70
+ },
71
+ {},
72
+ undefined,
73
+ { schema: 'sys' }
74
+ );
75
+
76
+ export const SysIndexColumns = defineTable(
77
+ 'index_columns',
78
+ {
79
+ object_id: col.int(),
80
+ index_id: col.int(),
81
+ column_id: col.int(),
82
+ key_ordinal: col.int()
83
+ },
84
+ {},
85
+ undefined,
86
+ { schema: 'sys' }
87
+ );
88
+
89
+ export const SysForeignKeys = defineTable(
90
+ 'foreign_keys',
91
+ {
92
+ object_id: col.int(),
93
+ name: col.varchar(255),
94
+ delete_referential_action_desc: col.varchar(64),
95
+ update_referential_action_desc: col.varchar(64)
96
+ },
97
+ {},
98
+ undefined,
99
+ { schema: 'sys' }
100
+ );
101
+
102
+ export const SysForeignKeyColumns = defineTable(
103
+ 'foreign_key_columns',
104
+ {
105
+ constraint_object_id: col.int(),
106
+ parent_object_id: col.int(),
107
+ parent_column_id: col.int(),
108
+ referenced_object_id: col.int(),
109
+ referenced_column_id: col.int(),
110
+ constraint_column_id: col.int()
111
+ },
112
+ {},
113
+ undefined,
114
+ { schema: 'sys' }
115
+ );
116
+
117
+ export default {
118
+ SysColumns,
119
+ SysTables,
120
+ SysSchemas,
121
+ SysTypes,
122
+ SysIndexes,
123
+ SysIndexColumns,
124
+ SysForeignKeys,
125
+ SysForeignKeyColumns
126
+ };
@@ -0,0 +1,89 @@
1
+ import { defineTable } from '../../../../schema/table.js';
2
+ import { col } from '../../../../schema/column-types.js';
3
+
4
+ const INFORMATION_SCHEMA = 'information_schema';
5
+
6
+ export const InformationSchemaTables = defineTable(
7
+ 'tables',
8
+ {
9
+ table_schema: col.varchar(255),
10
+ table_name: col.varchar(255),
11
+ table_comment: col.varchar(1024)
12
+ },
13
+ {},
14
+ undefined,
15
+ { schema: INFORMATION_SCHEMA }
16
+ );
17
+
18
+ export const InformationSchemaColumns = defineTable(
19
+ 'columns',
20
+ {
21
+ table_schema: col.varchar(255),
22
+ table_name: col.varchar(255),
23
+ column_name: col.varchar(255),
24
+ column_type: col.varchar(255),
25
+ data_type: col.varchar(255),
26
+ is_nullable: col.varchar(3),
27
+ column_default: col.varchar(1024),
28
+ extra: col.varchar(255),
29
+ column_comment: col.varchar(1024),
30
+ ordinal_position: col.int()
31
+ },
32
+ {},
33
+ undefined,
34
+ { schema: INFORMATION_SCHEMA }
35
+ );
36
+
37
+ export const InformationSchemaKeyColumnUsage = defineTable(
38
+ 'key_column_usage',
39
+ {
40
+ constraint_schema: col.varchar(255),
41
+ constraint_name: col.varchar(255),
42
+ table_schema: col.varchar(255),
43
+ table_name: col.varchar(255),
44
+ column_name: col.varchar(255),
45
+ ordinal_position: col.int(),
46
+ referenced_table_schema: col.varchar(255),
47
+ referenced_table_name: col.varchar(255),
48
+ referenced_column_name: col.varchar(255)
49
+ },
50
+ {},
51
+ undefined,
52
+ { schema: INFORMATION_SCHEMA }
53
+ );
54
+
55
+ export const InformationSchemaReferentialConstraints = defineTable(
56
+ 'referential_constraints',
57
+ {
58
+ constraint_schema: col.varchar(255),
59
+ constraint_name: col.varchar(255),
60
+ delete_rule: col.varchar(255),
61
+ update_rule: col.varchar(255)
62
+ },
63
+ {},
64
+ undefined,
65
+ { schema: INFORMATION_SCHEMA }
66
+ );
67
+
68
+ export const InformationSchemaStatistics = defineTable(
69
+ 'statistics',
70
+ {
71
+ table_schema: col.varchar(255),
72
+ table_name: col.varchar(255),
73
+ index_name: col.varchar(255),
74
+ non_unique: col.int(),
75
+ column_name: col.varchar(255),
76
+ seq_in_index: col.int()
77
+ },
78
+ {},
79
+ undefined,
80
+ { schema: INFORMATION_SCHEMA }
81
+ );
82
+
83
+ export default {
84
+ InformationSchemaTables,
85
+ InformationSchemaColumns,
86
+ InformationSchemaKeyColumnUsage,
87
+ InformationSchemaReferentialConstraints,
88
+ InformationSchemaStatistics
89
+ };
@@ -0,0 +1,47 @@
1
+ import { defineTable } from '../../../../schema/table.js';
2
+ import { col } from '../../../../schema/column-types.js';
3
+
4
+ // SQLite catalogs are limited; most metadata comes from PRAGMAs, but these tables are queryable.
5
+
6
+ export const SqliteMaster = defineTable(
7
+ 'sqlite_master',
8
+ {
9
+ type: col.varchar(255), // 'table', 'index', 'view', 'trigger'
10
+ name: col.varchar(255), // Object name
11
+ tbl_name: col.varchar(255), // Table the object belongs to
12
+ rootpage: col.int(), // B-tree root page
13
+ sql: col.varchar(4096) // Original DDL
14
+ },
15
+ {},
16
+ undefined,
17
+ { schema: undefined }
18
+ );
19
+
20
+ export const SqliteSequence = defineTable(
21
+ 'sqlite_sequence',
22
+ {
23
+ name: col.varchar(255), // Table name
24
+ seq: col.int() // Last autoincrement value
25
+ },
26
+ {},
27
+ undefined,
28
+ { schema: undefined }
29
+ );
30
+
31
+ export const SqliteStat1 = defineTable(
32
+ 'sqlite_stat1',
33
+ {
34
+ tbl: col.varchar(255), // Table name
35
+ idx: col.varchar(255), // Index name
36
+ stat: col.varchar(255) // Statistics string
37
+ },
38
+ {},
39
+ undefined,
40
+ { schema: undefined }
41
+ );
42
+
43
+ export default {
44
+ SqliteMaster,
45
+ SqliteSequence,
46
+ SqliteStat1
47
+ };
@@ -0,0 +1,84 @@
1
+ import { caseWhen, cast, div, eq, inList, valueToOperand, columnOperand, ValueOperandInput } from '../../../ast/expression-builders.js';
2
+ import { isOperandNode } from '../../../ast/expression-nodes.js';
3
+ import type { OperandNode } from '../../../ast/expression.js';
4
+ import type { ColumnRef } from '../../../ast/types.js';
5
+ import { concat, lower } from '../../../functions/text.js';
6
+
7
+ type OperandInput = OperandNode | ColumnRef | string | number | boolean | null;
8
+
9
+ const isColumnReference = (value: unknown): value is ColumnRef =>
10
+ typeof value === 'object' &&
11
+ value !== null &&
12
+ !('type' in value) &&
13
+ 'name' in value &&
14
+ typeof (value as ColumnRef).name === 'string';
15
+
16
+ const toOperandNode = (value: OperandInput): OperandNode => {
17
+ if (isOperandNode(value)) return value;
18
+ if (isColumnReference(value)) return columnOperand(value);
19
+ return valueToOperand(value as ValueOperandInput);
20
+ };
21
+
22
+ const fn = (name: string, args: OperandInput[]): OperandNode => ({
23
+ type: 'Function',
24
+ name,
25
+ fn: name,
26
+ args: args.map(arg => toOperandNode(arg))
27
+ });
28
+
29
+ const CHAR_TYPES = ['varchar', 'char', 'varbinary', 'binary', 'nvarchar', 'nchar'];
30
+ const DECIMAL_TYPES = ['decimal', 'numeric'];
31
+
32
+ export const objectDefinition = (objectId: OperandInput): OperandNode => fn('OBJECT_DEFINITION', [objectId]);
33
+
34
+ export const buildMssqlDataType = (
35
+ typeName: OperandInput,
36
+ maxLength: OperandInput,
37
+ precision: OperandInput,
38
+ scale: OperandInput
39
+ ): OperandNode => {
40
+ const typeOperand = toOperandNode(typeName);
41
+ const maxLenOperand = toOperandNode(maxLength);
42
+ const precisionOperand = toOperandNode(precision);
43
+ const scaleOperand = toOperandNode(scale);
44
+ const typeLower = lower(typeOperand);
45
+
46
+ const lengthCase = caseWhen(
47
+ [
48
+ {
49
+ when: eq(maxLenOperand, -1),
50
+ then: 'max'
51
+ },
52
+ {
53
+ when: inList(typeLower, ['nvarchar', 'nchar']),
54
+ then: cast(div(maxLenOperand, 2), 'varchar(10)')
55
+ }
56
+ ],
57
+ cast(maxLenOperand, 'varchar(10)')
58
+ );
59
+
60
+ const charSuffix = concat('(', lengthCase, ')');
61
+
62
+ const decimalSuffix = concat(
63
+ '(',
64
+ cast(precisionOperand, 'varchar(10)'),
65
+ ',',
66
+ cast(scaleOperand, 'varchar(10)'),
67
+ ')'
68
+ );
69
+
70
+ const suffix = caseWhen(
71
+ [
72
+ { when: inList(typeLower, CHAR_TYPES), then: charSuffix },
73
+ { when: inList(typeLower, DECIMAL_TYPES), then: decimalSuffix }
74
+ ],
75
+ ''
76
+ );
77
+
78
+ return concat(typeLower, suffix);
79
+ };
80
+
81
+ export default {
82
+ objectDefinition,
83
+ buildMssqlDataType
84
+ };