ormbridge 0.10.0 → 0.12.0
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/README.md +26 -19
- package/dist/cliRunner.js +2 -2
- package/dist/cliRunner.js.map +1 -1
- package/dist/convert.js +30 -1
- package/dist/convert.js.map +1 -1
- package/dist/emitters/cockroach.d.ts +33 -0
- package/dist/emitters/cockroach.js +81 -0
- package/dist/emitters/cockroach.js.map +1 -0
- package/dist/emitters/doctrine.js +11 -0
- package/dist/emitters/doctrine.js.map +1 -1
- package/dist/emitters/drizzle.js +39 -4
- package/dist/emitters/drizzle.js.map +1 -1
- package/dist/emitters/efcore.js +18 -1
- package/dist/emitters/efcore.js.map +1 -1
- package/dist/emitters/efcoreColumns.js +23 -2
- package/dist/emitters/efcoreColumns.js.map +1 -1
- package/dist/emitters/gorm.js +20 -1
- package/dist/emitters/gorm.js.map +1 -1
- package/dist/emitters/jpa.js +9 -0
- package/dist/emitters/jpa.js.map +1 -1
- package/dist/emitters/jpaColumns.d.ts +2 -0
- package/dist/emitters/jpaColumns.js +1 -0
- package/dist/emitters/jpaColumns.js.map +1 -1
- package/dist/emitters/jpaScalars.js +14 -1
- package/dist/emitters/jpaScalars.js.map +1 -1
- package/dist/emitters/laravel.js +11 -0
- package/dist/emitters/laravel.js.map +1 -1
- package/dist/emitters/neo4j.d.ts +28 -0
- package/dist/emitters/neo4j.js +211 -0
- package/dist/emitters/neo4j.js.map +1 -0
- package/dist/emitters/neo4jGraph.d.ts +57 -0
- package/dist/emitters/neo4jGraph.js +311 -0
- package/dist/emitters/neo4jGraph.js.map +1 -0
- package/dist/emitters/neo4jNames.d.ts +25 -0
- package/dist/emitters/neo4jNames.js +58 -0
- package/dist/emitters/neo4jNames.js.map +1 -0
- package/dist/emitters/neo4jRelationships.d.ts +15 -0
- package/dist/emitters/neo4jRelationships.js +240 -0
- package/dist/emitters/neo4jRelationships.js.map +1 -0
- package/dist/emitters/neo4jStatements.d.ts +41 -0
- package/dist/emitters/neo4jStatements.js +92 -0
- package/dist/emitters/neo4jStatements.js.map +1 -0
- package/dist/emitters/neo4jTypes.d.ts +31 -0
- package/dist/emitters/neo4jTypes.js +116 -0
- package/dist/emitters/neo4jTypes.js.map +1 -0
- package/dist/emitters/prisma.d.ts +1 -1
- package/dist/emitters/prisma.js +47 -5
- package/dist/emitters/prisma.js.map +1 -1
- package/dist/emitters/sequelize.js +82 -6
- package/dist/emitters/sequelize.js.map +1 -1
- package/dist/emitters/sqlClickhouse.d.ts +43 -0
- package/dist/emitters/sqlClickhouse.js +265 -0
- package/dist/emitters/sqlClickhouse.js.map +1 -0
- package/dist/emitters/sqlClickhouseIndexes.d.ts +3 -0
- package/dist/emitters/sqlClickhouseIndexes.js +98 -0
- package/dist/emitters/sqlClickhouseIndexes.js.map +1 -0
- package/dist/emitters/sqlClickhouseTypes.d.ts +39 -0
- package/dist/emitters/sqlClickhouseTypes.js +360 -0
- package/dist/emitters/sqlClickhouseTypes.js.map +1 -0
- package/dist/emitters/sqlDdl.d.ts +29 -1
- package/dist/emitters/sqlDdl.js +141 -21
- package/dist/emitters/sqlDdl.js.map +1 -1
- package/dist/emitters/sqlalchemy.d.ts +7 -1
- package/dist/emitters/sqlalchemy.js +39 -0
- package/dist/emitters/sqlalchemy.js.map +1 -1
- package/dist/emitters/typeorm.js +44 -9
- package/dist/emitters/typeorm.js.map +1 -1
- package/dist/formats.d.ts +2 -0
- package/dist/formats.js +26 -1
- package/dist/formats.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/parsers/prisma.js +9 -1
- package/dist/parsers/prisma.js.map +1 -1
- package/dist/parsers/sqlDdl.js +40 -2
- package/dist/parsers/sqlDdl.js.map +1 -1
- package/dist/parsers/sqlSyntax.js +15 -1
- package/dist/parsers/sqlSyntax.js.map +1 -1
- package/dist/parsers/sqlTypes.js +37 -2
- package/dist/parsers/sqlTypes.js.map +1 -1
- package/dist/parsers/typeorm.js +2 -0
- package/dist/parsers/typeorm.js.map +1 -1
- package/dist/styles.d.ts +3 -2
- package/dist/styles.js +4 -1
- package/dist/styles.js.map +1 -1
- package/package.json +1 -1
package/dist/emitters/sqlDdl.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { findModel } from '../ir.js';
|
|
2
2
|
import { limitIdentifier, toSnakeCase } from '../naming.js';
|
|
3
3
|
import { expandManyToMany, normalizeSchema, } from '../transforms.js';
|
|
4
|
+
import { COCKROACH_PREFIX, cockroachMissingType, cockroachSequentialWarning, } from './cockroach.js';
|
|
5
|
+
import { emitClickhouseDdl } from './sqlClickhouse.js';
|
|
4
6
|
/** The dialect a provider is written in. MongoDB has no SQL; CockroachDB speaks PostgreSQL. */
|
|
5
7
|
export function sqlDialectOf(provider) {
|
|
6
8
|
switch (provider) {
|
|
@@ -91,6 +93,10 @@ export function quoteIdentifier(dialect, name) {
|
|
|
91
93
|
}
|
|
92
94
|
return quoteAlways(dialect, name);
|
|
93
95
|
}
|
|
96
|
+
/** The name of the database in messages: CockroachDB shares the PostgreSQL dialect but not its limits. */
|
|
97
|
+
function labelOf(context) {
|
|
98
|
+
return context.cockroach ? 'CockroachDB' : DIALECT_LABELS[context.dialect];
|
|
99
|
+
}
|
|
94
100
|
function quoteAlways(dialect, name) {
|
|
95
101
|
switch (dialect) {
|
|
96
102
|
case 'mysql':
|
|
@@ -260,14 +266,19 @@ const UUID_DEFAULTS = {
|
|
|
260
266
|
// Entry point
|
|
261
267
|
// ---------------------------------------------------------------------------
|
|
262
268
|
export function emitSqlDdl(schema, options) {
|
|
269
|
+
if (options.provider === 'clickhouse') {
|
|
270
|
+
return emitClickhouseDdl(schema, options.naming);
|
|
271
|
+
}
|
|
263
272
|
// The join table of a many-to-many field is an ordinary table in SQL.
|
|
264
273
|
const expanded = expandManyToMany(schema);
|
|
265
274
|
const prepared = options.naming === 'normalize' ? normalizeSchema(expanded) : expanded;
|
|
266
275
|
const dialect = sqlDialectOf(options.provider);
|
|
267
276
|
const context = {
|
|
268
277
|
dialect,
|
|
278
|
+
cockroach: options.provider === 'cockroachdb',
|
|
269
279
|
schema: prepared,
|
|
270
280
|
warnings: [],
|
|
281
|
+
sequentialKeys: [],
|
|
271
282
|
enums: new Map(),
|
|
272
283
|
names: new Set(),
|
|
273
284
|
unexpressedAutoUpdates: [],
|
|
@@ -296,8 +307,11 @@ export function emitSqlDdl(schema, options) {
|
|
|
296
307
|
for (const view of views) {
|
|
297
308
|
context.warnings.push(`${view.name}: this is a database view; the schema has no SQL definition for it, so only a commented placeholder was written.`);
|
|
298
309
|
}
|
|
310
|
+
if (context.sequentialKeys.length > 0) {
|
|
311
|
+
context.warnings.push(cockroachSequentialWarning(context.sequentialKeys, 'written as GENERATED BY DEFAULT AS IDENTITY, which CockroachDB backs with a sequence (SERIAL would be unique_rowid(): 64-bit and not consecutive)'));
|
|
312
|
+
}
|
|
299
313
|
if (context.unexpressedAutoUpdates.length > 0) {
|
|
300
|
-
context.warnings.push(`${
|
|
314
|
+
context.warnings.push(`${labelOf(context)} cannot refresh a column on update from the DDL (${context.unexpressedAutoUpdates.join(', ')}); the application or a trigger has to set it.`);
|
|
301
315
|
}
|
|
302
316
|
return { text: render(context, ordered, views), warnings: context.warnings };
|
|
303
317
|
}
|
|
@@ -306,7 +320,7 @@ function warnAboutProvider(context, provider) {
|
|
|
306
320
|
context.warnings.push('Provider "mongodb": MongoDB has no SQL schema; PostgreSQL DDL was written.');
|
|
307
321
|
}
|
|
308
322
|
else if (provider === 'cockroachdb') {
|
|
309
|
-
context.warnings.push(
|
|
323
|
+
context.warnings.push(`${COCKROACH_PREFIX} PostgreSQL-compatible DDL was written for CockroachDB: integers are sized explicitly (INT4, INT2, INT8) because a bare INT is 64-bit there, and PostgreSQL-only types and index methods are replaced and reported below.`);
|
|
310
324
|
}
|
|
311
325
|
}
|
|
312
326
|
function warnAboutIgnoredConstructs(context) {
|
|
@@ -348,7 +362,7 @@ function generatedName(context, table, columns, suffix) {
|
|
|
348
362
|
function checkIdentifierLength(context, label, name) {
|
|
349
363
|
const max = MAX_IDENTIFIER[context.dialect];
|
|
350
364
|
if (context.dialect !== 'sqlite' && name.length > max) {
|
|
351
|
-
context.warnings.push(`${label}: the name "${name}" is longer than the ${max} characters ${
|
|
365
|
+
context.warnings.push(`${label}: the name "${name}" is longer than the ${max} characters ${labelOf(context)} allows in an identifier.`);
|
|
352
366
|
}
|
|
353
367
|
}
|
|
354
368
|
function resolveEnums(context, naming) {
|
|
@@ -408,6 +422,29 @@ function nativeTypeSql(dialect, field) {
|
|
|
408
422
|
const [first, ...rest] = known.split(' ');
|
|
409
423
|
return `${first ?? known}(${args.join(', ')})${rest.length > 0 ? ` ${rest.join(' ')}` : ''}`;
|
|
410
424
|
}
|
|
425
|
+
/**
|
|
426
|
+
* CockroachDB's reading of a Prisma `@db.*` native type, for the types that differ from PostgreSQL:
|
|
427
|
+
* `Integer` is spelled INT4 (a bare INTEGER is 64-bit), `Money` and `Xml` do not exist, and `Json` is
|
|
428
|
+
* stored as JSONB.
|
|
429
|
+
*/
|
|
430
|
+
function cockroachNativeType(context, label, field) {
|
|
431
|
+
const name = field.nativeType?.name.toLowerCase();
|
|
432
|
+
switch (name) {
|
|
433
|
+
case 'integer':
|
|
434
|
+
return plainType('INT4', field);
|
|
435
|
+
case 'money':
|
|
436
|
+
context.warnings.push(`${label}: CockroachDB has no MONEY type; the column was written as NUMERIC(19, 4).`);
|
|
437
|
+
return plainType('NUMERIC(19, 4)', field);
|
|
438
|
+
case 'xml':
|
|
439
|
+
context.warnings.push(`${label}: CockroachDB has no XML type; the column was written as TEXT.`);
|
|
440
|
+
return { ...plainType('TEXT', field), textual: true, storage: 'text' };
|
|
441
|
+
case 'json':
|
|
442
|
+
context.warnings.push(`${label}: CockroachDB stores JSON as JSONB (key order and duplicate keys are not kept); the column was written as JSONB.`);
|
|
443
|
+
return { ...plainType('JSONB', field), textual: true };
|
|
444
|
+
default:
|
|
445
|
+
return undefined;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
411
448
|
function storageOf(type) {
|
|
412
449
|
switch (type) {
|
|
413
450
|
case 'text':
|
|
@@ -437,6 +474,10 @@ function columnType(context, label, field, columnSql) {
|
|
|
437
474
|
if (depth === 0) {
|
|
438
475
|
return element;
|
|
439
476
|
}
|
|
477
|
+
if (context.cockroach && (depth > 1 || element.storage === 'json')) {
|
|
478
|
+
context.warnings.push(`${label}: CockroachDB has no ${depth > 1 ? 'multi-dimensional arrays' : 'arrays of JSON'}; the column was written as JSONB.`);
|
|
479
|
+
return jsonColumnType(context, columnSql, 'an array stored as JSON');
|
|
480
|
+
}
|
|
440
481
|
if (context.dialect === 'postgresql') {
|
|
441
482
|
return {
|
|
442
483
|
...element,
|
|
@@ -444,7 +485,7 @@ function columnType(context, label, field, columnSql) {
|
|
|
444
485
|
storage: 'other',
|
|
445
486
|
};
|
|
446
487
|
}
|
|
447
|
-
const label2 =
|
|
488
|
+
const label2 = labelOf(context);
|
|
448
489
|
context.warnings.push(`${label}: ${label2} has no array column type; the array was written as a JSON ${context.dialect === 'sqlite' ? 'text ' : ''}column.`);
|
|
449
490
|
return jsonColumnType(context, columnSql, 'an array stored as JSON');
|
|
450
491
|
}
|
|
@@ -498,6 +539,12 @@ function scalarColumnType(context, label, field, columnSql, isArrayElement) {
|
|
|
498
539
|
if (enumInfo !== undefined) {
|
|
499
540
|
return enumColumnType(context, enumInfo, columnSql, isArrayElement);
|
|
500
541
|
}
|
|
542
|
+
if (context.cockroach) {
|
|
543
|
+
const cockroachNative = cockroachNativeType(context, label, field);
|
|
544
|
+
if (cockroachNative !== undefined) {
|
|
545
|
+
return cockroachNative;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
501
548
|
const native = nativeTypeSql(dialect, field);
|
|
502
549
|
if (native !== undefined) {
|
|
503
550
|
return {
|
|
@@ -534,7 +581,12 @@ function scalarColumnType(context, label, field, columnSql, isArrayElement) {
|
|
|
534
581
|
textual: true,
|
|
535
582
|
};
|
|
536
583
|
case 'int':
|
|
537
|
-
|
|
584
|
+
// A bare INT (INTEGER) is 64-bit on CockroachDB, so the 32-bit IR type is spelled INT4.
|
|
585
|
+
return plainType(context.cockroach
|
|
586
|
+
? 'INT4'
|
|
587
|
+
: dialect === 'mysql' || dialect === 'sqlserver'
|
|
588
|
+
? 'INT'
|
|
589
|
+
: 'INTEGER', field);
|
|
538
590
|
case 'bigInt':
|
|
539
591
|
return plainType('BIGINT', field);
|
|
540
592
|
case 'float':
|
|
@@ -581,7 +633,7 @@ function scalarColumnType(context, label, field, columnSql, isArrayElement) {
|
|
|
581
633
|
if (dialect === 'postgresql') {
|
|
582
634
|
return plainType('INTERVAL', field);
|
|
583
635
|
}
|
|
584
|
-
context.warnings.push(`${label}: ${
|
|
636
|
+
context.warnings.push(`${label}: ${labelOf(context)} has no interval type; the duration was written as an integer (microseconds).`);
|
|
585
637
|
return {
|
|
586
638
|
...plainType(dialect === 'sqlite' ? 'INTEGER' : 'BIGINT', field),
|
|
587
639
|
comment: 'duration in microseconds',
|
|
@@ -598,6 +650,10 @@ function scalarColumnType(context, label, field, columnSql, isArrayElement) {
|
|
|
598
650
|
textual: true,
|
|
599
651
|
};
|
|
600
652
|
case 'hstore':
|
|
653
|
+
if (context.cockroach) {
|
|
654
|
+
context.warnings.push(`${label}: CockroachDB has no hstore type; the column was written as JSONB.`);
|
|
655
|
+
return jsonColumnType(context, columnSql, 'hstore stored as JSONB');
|
|
656
|
+
}
|
|
601
657
|
if (dialect === 'postgresql') {
|
|
602
658
|
context.needsHstore = true;
|
|
603
659
|
return { ...plainType('HSTORE', field), textual: true };
|
|
@@ -666,7 +722,7 @@ function decimalType(context, label, field) {
|
|
|
666
722
|
}
|
|
667
723
|
const limit = dialect === 'mysql' ? 65 : dialect === 'sqlserver' ? 38 : undefined;
|
|
668
724
|
if (precision !== undefined && limit !== undefined && precision > limit) {
|
|
669
|
-
context.warnings.push(`${label}: ${
|
|
725
|
+
context.warnings.push(`${label}: ${labelOf(context)} allows at most ${limit} digits in a decimal; ${precision} was reduced to ${limit}.`);
|
|
670
726
|
precision = limit;
|
|
671
727
|
if (scale !== undefined && scale > limit) {
|
|
672
728
|
scale = limit;
|
|
@@ -689,6 +745,14 @@ function decimalType(context, label, field) {
|
|
|
689
745
|
]);
|
|
690
746
|
}
|
|
691
747
|
function rangeColumnType(context, label, field) {
|
|
748
|
+
if (context.cockroach) {
|
|
749
|
+
context.warnings.push(`${label}: CockroachDB has no range types (int4range, tstzrange, ...); the column was written as text.`);
|
|
750
|
+
return {
|
|
751
|
+
...plainType('TEXT', field),
|
|
752
|
+
comment: 'range stored as text',
|
|
753
|
+
textual: true,
|
|
754
|
+
};
|
|
755
|
+
}
|
|
692
756
|
if (context.dialect === 'postgresql') {
|
|
693
757
|
return {
|
|
694
758
|
...plainType(POSTGRES_RANGE_TYPES[field.rangeOf ?? 'int'] ?? 'INT4RANGE', field),
|
|
@@ -711,7 +775,18 @@ function rangeColumnType(context, label, field) {
|
|
|
711
775
|
}
|
|
712
776
|
function unsupportedColumnType(context, label, field) {
|
|
713
777
|
if (field.unsupportedType !== undefined && field.unsupportedType !== '') {
|
|
714
|
-
|
|
778
|
+
const missing = context.cockroach
|
|
779
|
+
? cockroachMissingType(field.unsupportedType)
|
|
780
|
+
: undefined;
|
|
781
|
+
if (missing !== undefined) {
|
|
782
|
+
context.warnings.push(`${label}: CockroachDB has no ${missing} type; "${field.unsupportedType}" was written as it is, so CREATE TABLE fails until the column type is changed.`);
|
|
783
|
+
return {
|
|
784
|
+
...plainType(field.unsupportedType, field),
|
|
785
|
+
storage: 'other',
|
|
786
|
+
textual: false,
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
context.warnings.push(`${label}: the database type "${field.unsupportedType}" (Prisma Unsupported) was written as it is; check that ${labelOf(context)} has it.`);
|
|
715
790
|
return {
|
|
716
791
|
...plainType(field.unsupportedType, field),
|
|
717
792
|
storage: 'other',
|
|
@@ -885,11 +960,15 @@ function defaultSql(context, label, field, type) {
|
|
|
885
960
|
if (value.isFunction === true) {
|
|
886
961
|
return dropped(`the ${value.expression} default function has no SQL equivalent`);
|
|
887
962
|
}
|
|
963
|
+
if (context.cockroach &&
|
|
964
|
+
value.expression.replace(/\s+/g, '').toLowerCase() === 'unique_rowid()') {
|
|
965
|
+
return 'unique_rowid()';
|
|
966
|
+
}
|
|
888
967
|
const known = knownExpression(dialect, field, value.expression, type);
|
|
889
968
|
if (known !== undefined) {
|
|
890
969
|
return known;
|
|
891
970
|
}
|
|
892
|
-
context.warnings.push(`${label}: the database default expression ${value.expression} was written as it is; check that ${
|
|
971
|
+
context.warnings.push(`${label}: the database default expression ${value.expression} was written as it is; check that ${labelOf(context)} accepts it.`);
|
|
893
972
|
const expression = value.expression.trim();
|
|
894
973
|
return expression.startsWith('(') ? expression : `(${expression})`;
|
|
895
974
|
}
|
|
@@ -979,6 +1058,12 @@ function literalDefault(context, label, field, value, type) {
|
|
|
979
1058
|
case 'bytes':
|
|
980
1059
|
return dropped('a literal default on a binary column cannot be written');
|
|
981
1060
|
case 'hstore': {
|
|
1061
|
+
if (context.cockroach) {
|
|
1062
|
+
const asJson = parseJson(text);
|
|
1063
|
+
return asJson.ok
|
|
1064
|
+
? `${sqlStringLiteral(dialect, JSON.stringify(asJson.value))}::jsonb`
|
|
1065
|
+
: dropped(`the value ${text} is not JSON`);
|
|
1066
|
+
}
|
|
982
1067
|
if (dialect === 'postgresql') {
|
|
983
1068
|
return text === '{}'
|
|
984
1069
|
? "''::hstore"
|
|
@@ -1002,7 +1087,7 @@ function isIntegerKey(field) {
|
|
|
1002
1087
|
return field.type === 'int' || field.type === 'bigInt';
|
|
1003
1088
|
}
|
|
1004
1089
|
/** Resolves the column a foreign key points at: `toField`, otherwise the target's single-column key. */
|
|
1005
|
-
function resolveTarget(context, target, toField, depth = 0) {
|
|
1090
|
+
export function resolveTarget(context, target, toField, depth = 0) {
|
|
1006
1091
|
if (depth > 5) {
|
|
1007
1092
|
return undefined;
|
|
1008
1093
|
}
|
|
@@ -1053,7 +1138,7 @@ function resolveRelationColumn(context, owner, relation, depth) {
|
|
|
1053
1138
|
// ---------------------------------------------------------------------------
|
|
1054
1139
|
// Tables
|
|
1055
1140
|
// ---------------------------------------------------------------------------
|
|
1056
|
-
function columnNameOf(model, name) {
|
|
1141
|
+
export function columnNameOf(model, name) {
|
|
1057
1142
|
const relation = model.relations.find((candidate) => candidate.kind !== 'manyToMany' && candidate.name === name) ??
|
|
1058
1143
|
model.relations.find((candidate) => candidate.kind !== 'manyToMany' && candidate.columnName === name);
|
|
1059
1144
|
const field = model.fields.find((candidate) => candidate.name === name) ??
|
|
@@ -1061,8 +1146,15 @@ function columnNameOf(model, name) {
|
|
|
1061
1146
|
// A field wins over a relation of the same name: the field is the column the key is written on.
|
|
1062
1147
|
return field?.columnName ?? relation?.columnName;
|
|
1063
1148
|
}
|
|
1064
|
-
function isAutoIncrement(field) {
|
|
1065
|
-
|
|
1149
|
+
function isAutoIncrement(context, field) {
|
|
1150
|
+
if (field.default?.kind === 'autoIncrement') {
|
|
1151
|
+
return true;
|
|
1152
|
+
}
|
|
1153
|
+
// Prisma's CockroachDB `sequence()` default is the SERIAL of that database.
|
|
1154
|
+
return (context.cockroach &&
|
|
1155
|
+
field.default?.kind === 'dbExpression' &&
|
|
1156
|
+
field.default.isFunction === true &&
|
|
1157
|
+
/^sequence\(/i.test(field.default.expression));
|
|
1066
1158
|
}
|
|
1067
1159
|
function buildTable(context, model) {
|
|
1068
1160
|
const dialect = context.dialect;
|
|
@@ -1226,7 +1318,7 @@ function buildColumn(context, label, field, columnName, inKey, singleColumnKey)
|
|
|
1226
1318
|
notes.push('generated column written as a regular column');
|
|
1227
1319
|
}
|
|
1228
1320
|
let autoIncrement = false;
|
|
1229
|
-
if (isAutoIncrement(field)) {
|
|
1321
|
+
if (isAutoIncrement(context, field)) {
|
|
1230
1322
|
if (!isIntegerKey(field)) {
|
|
1231
1323
|
context.warnings.push(`${label}: an auto-increment default on a ${field.type} column cannot be written; it was dropped.`);
|
|
1232
1324
|
}
|
|
@@ -1238,6 +1330,9 @@ function buildColumn(context, label, field, columnName, inKey, singleColumnKey)
|
|
|
1238
1330
|
}
|
|
1239
1331
|
else {
|
|
1240
1332
|
autoIncrement = true;
|
|
1333
|
+
if (context.cockroach) {
|
|
1334
|
+
context.sequentialKeys.push(label);
|
|
1335
|
+
}
|
|
1241
1336
|
}
|
|
1242
1337
|
}
|
|
1243
1338
|
let sqlType = type.sql;
|
|
@@ -1254,6 +1349,11 @@ function buildColumn(context, label, field, columnName, inKey, singleColumnKey)
|
|
|
1254
1349
|
onUpdate = mysqlCurrentTimestamp(type.sql);
|
|
1255
1350
|
defaultText ??= onUpdate;
|
|
1256
1351
|
}
|
|
1352
|
+
else if (context.cockroach && field.type === 'dateTime') {
|
|
1353
|
+
// CockroachDB (unlike PostgreSQL) has column-level ON UPDATE.
|
|
1354
|
+
onUpdate = NOW_DEFAULTS[dialect].dateTime;
|
|
1355
|
+
defaultText ??= onUpdate;
|
|
1356
|
+
}
|
|
1257
1357
|
else {
|
|
1258
1358
|
context.unexpressedAutoUpdates.push(label);
|
|
1259
1359
|
}
|
|
@@ -1442,7 +1542,10 @@ function indexColumnSql(context, table, label, column, options) {
|
|
|
1442
1542
|
else if (options?.length !== undefined) {
|
|
1443
1543
|
context.warnings.push(`${label}: the prefix length of "${column}" is only supported by MySQL and was dropped.`);
|
|
1444
1544
|
}
|
|
1445
|
-
if (context.
|
|
1545
|
+
if (context.cockroach && options?.ops !== undefined) {
|
|
1546
|
+
context.warnings.push(`${label}: CockroachDB supports only trigram operator classes on inverted indexes; the operator class of "${column}" was dropped.`);
|
|
1547
|
+
}
|
|
1548
|
+
else if (context.dialect === 'postgresql' && options?.ops !== undefined) {
|
|
1446
1549
|
const raw = /^raw\(\s*["'](.*)["']\s*\)$/.exec(options.ops);
|
|
1447
1550
|
text += ` ${raw?.[1] ?? toSnakeCase(options.ops)}`;
|
|
1448
1551
|
}
|
|
@@ -1476,7 +1579,7 @@ function buildIndexes(context, table) {
|
|
|
1476
1579
|
const blocked = columns.find((column) => table.columns.find((candidate) => candidate.name === column)
|
|
1477
1580
|
?.unindexable === true);
|
|
1478
1581
|
if (blocked !== undefined) {
|
|
1479
|
-
context.warnings.push(`${label}: ${
|
|
1582
|
+
context.warnings.push(`${label}: ${labelOf(context)} cannot index the column "${blocked}" (its type is unbounded or JSON); the index was skipped.`);
|
|
1480
1583
|
continue;
|
|
1481
1584
|
}
|
|
1482
1585
|
const name = claimName(context, index.name ??
|
|
@@ -1499,7 +1602,7 @@ function buildIndexes(context, table) {
|
|
|
1499
1602
|
const blocked = unique.columns.find((column) => table.columns.find((candidate) => candidate.name === column)
|
|
1500
1603
|
?.unindexable === true);
|
|
1501
1604
|
if (blocked !== undefined) {
|
|
1502
|
-
context.warnings.push(`${model.name}.${blocked}: ${
|
|
1605
|
+
context.warnings.push(`${model.name}.${blocked}: ${labelOf(context)} cannot index the column "${blocked}" (its type is unbounded or JSON); the unique constraint was skipped.`);
|
|
1503
1606
|
}
|
|
1504
1607
|
return blocked === undefined;
|
|
1505
1608
|
});
|
|
@@ -1532,12 +1635,13 @@ function indexStatement(context, table, label, index, name, columns, entries) {
|
|
|
1532
1635
|
return `CREATE FULLTEXT INDEX ${nameSql} ON ${tableSql} (${list});`;
|
|
1533
1636
|
}
|
|
1534
1637
|
if (dialect === 'postgresql') {
|
|
1638
|
+
// CockroachDB builds the same inverted index over to_tsvector() (checked on v26.3).
|
|
1535
1639
|
const vector = columns
|
|
1536
1640
|
.map((column) => `coalesce(${quote(context, column)}::text, '')`)
|
|
1537
1641
|
.join(" || ' ' || ");
|
|
1538
1642
|
return `CREATE INDEX ${nameSql} ON ${tableSql} USING gin (to_tsvector('simple', ${vector}));`;
|
|
1539
1643
|
}
|
|
1540
|
-
context.warnings.push(`${label}: ${
|
|
1644
|
+
context.warnings.push(`${label}: ${labelOf(context)} full-text search needs a separate mechanism (a virtual table or a catalog); it was written as an ordinary index.`);
|
|
1541
1645
|
}
|
|
1542
1646
|
const parts = ['CREATE'];
|
|
1543
1647
|
if (index.isUnique) {
|
|
@@ -1555,7 +1659,23 @@ function indexStatement(context, table, label, index, name, columns, entries) {
|
|
|
1555
1659
|
let trailing = '';
|
|
1556
1660
|
if (index.method !== undefined) {
|
|
1557
1661
|
const method = index.method.toUpperCase();
|
|
1558
|
-
if (
|
|
1662
|
+
if (context.cockroach) {
|
|
1663
|
+
const last = columns[columns.length - 1];
|
|
1664
|
+
const lastType = table.columns.find((candidate) => candidate.name === last)
|
|
1665
|
+
?.sqlType ?? '';
|
|
1666
|
+
if (method === 'GIN') {
|
|
1667
|
+
if (/JSONB|\[\]$|TSVECTOR|GEOMETRY|GEOGRAPHY/i.test(lastType)) {
|
|
1668
|
+
target = `${tableSql} USING gin`;
|
|
1669
|
+
}
|
|
1670
|
+
else {
|
|
1671
|
+
context.warnings.push(`${label}: CockroachDB builds an inverted (GIN) index only over JSONB, array, tsvector or spatial columns; "${last ?? ''}" is ${lastType}, so an ordinary index was written.`);
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
else if (method !== 'BTREE') {
|
|
1675
|
+
context.warnings.push(`${label}: CockroachDB has no ${index.method} indexes (only B-tree and inverted GIN); an ordinary index was written${method === 'HASH' ? ' (CREATE INDEX ... USING HASH is a hash-sharded B-tree there, a different feature)' : ''}.`);
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
else if (dialect === 'postgresql') {
|
|
1559
1679
|
target = `${tableSql} USING ${method.toLowerCase()}`;
|
|
1560
1680
|
}
|
|
1561
1681
|
else if (dialect === 'mysql' &&
|
|
@@ -1564,7 +1684,7 @@ function indexStatement(context, table, label, index, name, columns, entries) {
|
|
|
1564
1684
|
}
|
|
1565
1685
|
else if (method !== 'BTREE') {
|
|
1566
1686
|
// B-tree is what every database builds anyway.
|
|
1567
|
-
context.warnings.push(`${label}: the index type ${index.method} is not supported by ${
|
|
1687
|
+
context.warnings.push(`${label}: the index type ${index.method} is not supported by ${labelOf(context)} and was dropped.`);
|
|
1568
1688
|
}
|
|
1569
1689
|
}
|
|
1570
1690
|
parts.push('INDEX', nameSql, 'ON', target);
|
|
@@ -1698,7 +1818,7 @@ function renderView(context, view) {
|
|
|
1698
1818
|
function render(context, ordered, views) {
|
|
1699
1819
|
const dialect = context.dialect;
|
|
1700
1820
|
const blocks = [
|
|
1701
|
-
`-- SQL DDL for ${
|
|
1821
|
+
`-- SQL DDL for ${labelOf(context)}, generated by ormbridge.`,
|
|
1702
1822
|
];
|
|
1703
1823
|
if (dialect === 'sqlite') {
|
|
1704
1824
|
blocks.push('PRAGMA foreign_keys = ON;');
|