@dbml/connector 5.0.0 → 5.1.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/__tests__/connectors/mssql/expect-out-files/schema.json +140 -106
- package/__tests__/connectors/mysql/expect-out-files/schema.json +40 -0
- package/__tests__/connectors/postgres/expect-out-files/schema.json +100 -0
- package/dist/connectors/bigqueryConnector.js +4 -2
- package/dist/connectors/mssqlConnector.js +70 -140
- package/dist/connectors/mysqlConnector.js +66 -7
- package/dist/connectors/postgresConnector.d.ts +1 -1
- package/dist/connectors/postgresConnector.js +97 -29
- package/dist/connectors/snowflakeConnector.js +8 -5
- package/dist/connectors/types.d.ts +10 -6
- package/dist/utils/helpers.d.ts +2 -1
- package/dist/utils/helpers.js +14 -3
- package/package.json +2 -2
- package/src/connectors/bigqueryConnector.ts +4 -2
- package/src/connectors/mssqlConnector.ts +74 -149
- package/src/connectors/mysqlConnector.ts +76 -11
- package/src/connectors/postgresConnector.ts +104 -29
- package/src/connectors/snowflakeConnector.ts +19 -15
- package/src/connectors/types.ts +16 -61
- package/src/utils/helpers.ts +30 -12
- package/__tests__/connectors/mssql/out-files/schema.json +0 -2074
- package/__tests__/connectors/mysql/out-files/schema.json +0 -1430
- package/__tests__/connectors/postgres/out-files/schema.json +0 -1719
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { createConnection, Connection } from 'mysql2/promise';
|
|
2
2
|
import { flatten } from 'lodash';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
CheckConstraintDictionary, DefaultInfo, DatabaseSchema, DefaultType, Field, IndexColumn, FieldsDictionary, Table, Enum, IndexesDictionary, TableConstraintsDictionary, Index, Ref,
|
|
5
|
+
} from './types';
|
|
4
6
|
|
|
5
7
|
const NUMBER_REGEX = '^-?[0-9]+(.[0-9]+)?$';
|
|
6
8
|
|
|
@@ -221,7 +223,7 @@ async function generateEnums (client: Connection, schemaName: string): Promise<E
|
|
|
221
223
|
}
|
|
222
224
|
|
|
223
225
|
/**
|
|
224
|
-
* Mysql
|
|
226
|
+
* Mysql automatically creates index for primary keys, foreign keys, unique constraint. -> Ignore
|
|
225
227
|
*/
|
|
226
228
|
async function generateIndexes (client: Connection, schemaName: string): Promise<IndexesDictionary> {
|
|
227
229
|
const query = `
|
|
@@ -321,9 +323,9 @@ async function generatePrimaryAndUniqueConstraint (client: Connection, schemaNam
|
|
|
321
323
|
const rows: any = queryResponse[0];
|
|
322
324
|
|
|
323
325
|
const inlineConstraintList = rows.filter((constraint: Record<string, any>) => constraint.columnCount === 1);
|
|
324
|
-
const
|
|
326
|
+
const outOfLineConstraintList = rows.filter((constraint: Record<string, any>) => constraint.columnCount > 1);
|
|
325
327
|
|
|
326
|
-
const compositeTableConstraintMap =
|
|
328
|
+
const compositeTableConstraintMap = outOfLineConstraintList.reduce((acc: any, row: Record<string, any>) => {
|
|
327
329
|
const {
|
|
328
330
|
tableName, constraintName, columnNames, constraintType,
|
|
329
331
|
} = row;
|
|
@@ -355,12 +357,12 @@ async function generatePrimaryAndUniqueConstraint (client: Connection, schemaNam
|
|
|
355
357
|
return acc;
|
|
356
358
|
}, {});
|
|
357
359
|
|
|
358
|
-
const compositeConstraintMap:
|
|
360
|
+
const compositeConstraintMap: IndexesDictionary = {};
|
|
359
361
|
Object.keys(compositeTableConstraintMap).forEach((tableName) => {
|
|
360
362
|
compositeConstraintMap[tableName] = flatten(Object.values(compositeTableConstraintMap[tableName]));
|
|
361
363
|
});
|
|
362
364
|
|
|
363
|
-
const constraintMap = inlineConstraintList.reduce((acc:
|
|
365
|
+
const constraintMap = inlineConstraintList.reduce((acc: TableConstraintsDictionary, row: Record<string, any>) => {
|
|
364
366
|
const { tableName, columnNames, constraintType } = row;
|
|
365
367
|
|
|
366
368
|
const key = tableName;
|
|
@@ -372,7 +374,7 @@ async function generatePrimaryAndUniqueConstraint (client: Connection, schemaNam
|
|
|
372
374
|
|
|
373
375
|
columnList.forEach((columnName: string) => {
|
|
374
376
|
if (!acc[key][columnName]) {
|
|
375
|
-
acc[key][columnName] = {};
|
|
377
|
+
acc[key][columnName] = { checks: [] };
|
|
376
378
|
}
|
|
377
379
|
|
|
378
380
|
if (constraintType === 'PRIMARY KEY') {
|
|
@@ -386,7 +388,58 @@ async function generatePrimaryAndUniqueConstraint (client: Connection, schemaNam
|
|
|
386
388
|
return acc;
|
|
387
389
|
}, {});
|
|
388
390
|
|
|
389
|
-
return { compositeConstraintMap, constraintMap };
|
|
391
|
+
return { compositeConstraintMap, primaryAndUniqueConstraintMap: constraintMap };
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function extractColumnsFromCheckConstraint (checkClause: string): string[] {
|
|
395
|
+
return [...checkClause.matchAll(/`(?:``|[^`])*`/g)].map((col) => col.toString().slice(1, -1));
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
async function generateCheckConstraints (client: Connection, schemaName: string): Promise<{ checks: CheckConstraintDictionary, tableConstraints: TableConstraintsDictionary }> {
|
|
399
|
+
const query = `
|
|
400
|
+
select
|
|
401
|
+
cc.constraint_name as constraintName,
|
|
402
|
+
tc.table_name as tableName,
|
|
403
|
+
cc.check_clause as checkClause
|
|
404
|
+
from information_schema.check_constraints cc
|
|
405
|
+
join information_schema.table_constraints tc
|
|
406
|
+
on cc.constraint_name = tc.constraint_name
|
|
407
|
+
where cc.constraint_schema = ?
|
|
408
|
+
`;
|
|
409
|
+
|
|
410
|
+
const queryResponse = await client.query(query, [schemaName]);
|
|
411
|
+
const rows: any = queryResponse[0];
|
|
412
|
+
const checks: CheckConstraintDictionary = {};
|
|
413
|
+
const tableConstraints: TableConstraintsDictionary = {};
|
|
414
|
+
rows.forEach((row: any) => {
|
|
415
|
+
const {
|
|
416
|
+
constraintName,
|
|
417
|
+
tableName,
|
|
418
|
+
checkClause,
|
|
419
|
+
} = row;
|
|
420
|
+
const key = tableName;
|
|
421
|
+
const cols = extractColumnsFromCheckConstraint(checkClause);
|
|
422
|
+
if (cols.length === 1) {
|
|
423
|
+
const col = cols[0];
|
|
424
|
+
if (!tableConstraints[key]) tableConstraints[key] = {};
|
|
425
|
+
if (!tableConstraints[key][col]) tableConstraints[key][col] = { checks: [] };
|
|
426
|
+
tableConstraints[key][col].checks.push({
|
|
427
|
+
name: constraintName,
|
|
428
|
+
// The check definition has the form: `(expr)`
|
|
429
|
+
// The expression starts at 1 and ends at -1
|
|
430
|
+
expression: checkClause.slice(1, -1),
|
|
431
|
+
});
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
if (!checks[key]) checks[key] = [];
|
|
435
|
+
checks[key].push({
|
|
436
|
+
name: constraintName,
|
|
437
|
+
// The check definition has the form: `(expr)`
|
|
438
|
+
// The expression starts at 1 and ends at -1
|
|
439
|
+
expression: checkClause.slice(1, -1),
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
return { checks, tableConstraints };
|
|
390
443
|
}
|
|
391
444
|
|
|
392
445
|
async function generateForeignKeys (client: Connection, schemaName: string): Promise<Ref[]> {
|
|
@@ -453,7 +506,7 @@ async function generateForeignKeys (client: Connection, schemaName: string): Pro
|
|
|
453
506
|
return foreignKeyList;
|
|
454
507
|
}
|
|
455
508
|
|
|
456
|
-
function combineIndexAndCompositeConstraint (userDefinedIndexMap:
|
|
509
|
+
function combineIndexAndCompositeConstraint (userDefinedIndexMap: IndexesDictionary, compositeConstraintMap: IndexesDictionary): IndexesDictionary {
|
|
457
510
|
const indexMap = Object.assign(userDefinedIndexMap, {});
|
|
458
511
|
|
|
459
512
|
Object.keys(compositeConstraintMap).forEach((tableName) => {
|
|
@@ -484,6 +537,7 @@ async function fetchSchemaJson (connection: string): Promise<DatabaseSchema> {
|
|
|
484
537
|
generateIndexes(client, schemaName),
|
|
485
538
|
generatePrimaryAndUniqueConstraint(client, schemaName),
|
|
486
539
|
generateForeignKeys(client, schemaName),
|
|
540
|
+
generateCheckConstraints(client, schemaName),
|
|
487
541
|
]);
|
|
488
542
|
client.end();
|
|
489
543
|
|
|
@@ -491,20 +545,31 @@ async function fetchSchemaJson (connection: string): Promise<DatabaseSchema> {
|
|
|
491
545
|
{ tableList, fieldMap },
|
|
492
546
|
enumList,
|
|
493
547
|
rawIndexMap,
|
|
494
|
-
{
|
|
548
|
+
{ primaryAndUniqueConstraintMap, compositeConstraintMap },
|
|
495
549
|
foreignKeyList,
|
|
550
|
+
{ checks: tableCheckConstraintMap, tableConstraints: columnCheckConstraintMap },
|
|
496
551
|
] = result;
|
|
497
552
|
|
|
498
553
|
// combine normal index and composite key
|
|
499
554
|
const indexMap = combineIndexAndCompositeConstraint(rawIndexMap, compositeConstraintMap);
|
|
500
555
|
|
|
556
|
+
const tableConstraints: TableConstraintsDictionary = { ...primaryAndUniqueConstraintMap };
|
|
557
|
+
Object.keys(columnCheckConstraintMap).forEach((tableName) => {
|
|
558
|
+
if (!tableConstraints[tableName]) tableConstraints[tableName] = {};
|
|
559
|
+
Object.keys(columnCheckConstraintMap[tableName]).forEach((colName) => {
|
|
560
|
+
if (!tableConstraints[tableName][colName]) tableConstraints[tableName][colName] = { checks: [] };
|
|
561
|
+
tableConstraints[tableName][colName].checks.push(...columnCheckConstraintMap[tableName][colName].checks);
|
|
562
|
+
});
|
|
563
|
+
});
|
|
564
|
+
|
|
501
565
|
return {
|
|
502
566
|
tables: tableList,
|
|
503
567
|
fields: fieldMap,
|
|
504
568
|
refs: foreignKeyList,
|
|
505
569
|
enums: enumList,
|
|
506
570
|
indexes: indexMap,
|
|
507
|
-
tableConstraints
|
|
571
|
+
tableConstraints,
|
|
572
|
+
checks: tableCheckConstraintMap,
|
|
508
573
|
};
|
|
509
574
|
}
|
|
510
575
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Client } from 'pg';
|
|
3
3
|
import { buildSchemaQuery, parseConnectionString } from '../utils/parseSchema';
|
|
4
4
|
import {
|
|
5
|
+
CheckConstraintDictionary,
|
|
5
6
|
DatabaseSchema,
|
|
6
7
|
DefaultInfo,
|
|
7
8
|
DefaultType,
|
|
@@ -301,7 +302,7 @@ const generateRawRefs = async (client: Client, schemas: string[]): Promise<Ref[]
|
|
|
301
302
|
return refs;
|
|
302
303
|
};
|
|
303
304
|
|
|
304
|
-
const
|
|
305
|
+
const generateIndexesAndConstraints = async (client: Client, schemas: string[]) => {
|
|
305
306
|
const indexListSql = `
|
|
306
307
|
WITH user_tables AS (
|
|
307
308
|
SELECT
|
|
@@ -348,8 +349,8 @@ const generateIndexes = async (client: Client, schemas: string[]) => {
|
|
|
348
349
|
ii.is_unique,
|
|
349
350
|
ii.is_primary,
|
|
350
351
|
ii.index_type,
|
|
351
|
-
ii.columns,
|
|
352
|
-
ii.expressions,
|
|
352
|
+
ii.columns AS index_columns,
|
|
353
|
+
ii.expressions AS index_expressions,
|
|
353
354
|
ii.constraint_type -- Retained constraint type here
|
|
354
355
|
FROM
|
|
355
356
|
user_tables ut
|
|
@@ -364,38 +365,38 @@ const generateIndexes = async (client: Client, schemas: string[]) => {
|
|
|
364
365
|
;
|
|
365
366
|
`;
|
|
366
367
|
const indexListResult = await client.query(indexListSql);
|
|
367
|
-
const {
|
|
368
|
-
const { constraint_type,
|
|
368
|
+
const { outOfLineIndexes, inlineIndexes } = indexListResult.rows.reduce((acc, row) => {
|
|
369
|
+
const { constraint_type, index_columns } = row;
|
|
369
370
|
|
|
370
|
-
if (
|
|
371
|
+
if (index_columns === 'null' || index_columns.trim() === '') return acc;
|
|
371
372
|
|
|
372
|
-
const isSingleColumn =
|
|
373
|
-
const
|
|
374
|
-
if (
|
|
375
|
-
acc.
|
|
373
|
+
const isSingleColumn = index_columns.split(',').length === 1;
|
|
374
|
+
const isInlineIndex = isSingleColumn && (constraint_type === 'PRIMARY KEY' || constraint_type === 'UNIQUE');
|
|
375
|
+
if (isInlineIndex) {
|
|
376
|
+
acc.inlineIndexes.push(row);
|
|
376
377
|
} else {
|
|
377
|
-
acc.
|
|
378
|
+
acc.outOfLineIndexes.push(row);
|
|
378
379
|
}
|
|
379
380
|
return acc;
|
|
380
|
-
}, {
|
|
381
|
+
}, { outOfLineIndexes: [], inlineIndexes: [] });
|
|
381
382
|
|
|
382
|
-
const indexes =
|
|
383
|
+
const indexes = outOfLineIndexes.reduce((acc: IndexesDictionary, indexRow: Record<string, any>) => {
|
|
383
384
|
const {
|
|
384
385
|
table_schema,
|
|
385
386
|
table_name,
|
|
386
387
|
index_name,
|
|
387
388
|
index_type,
|
|
388
|
-
|
|
389
|
-
|
|
389
|
+
index_columns,
|
|
390
|
+
index_expressions,
|
|
390
391
|
} = indexRow;
|
|
391
|
-
const indexColumns =
|
|
392
|
+
const indexColumns = index_columns.split(',').map((column: string) => {
|
|
392
393
|
return {
|
|
393
394
|
type: 'column',
|
|
394
395
|
value: column.trim(),
|
|
395
396
|
};
|
|
396
397
|
});
|
|
397
398
|
|
|
398
|
-
const indexExpressions =
|
|
399
|
+
const indexExpressions = index_expressions ? index_expressions.split(',').map((expression: string) => {
|
|
399
400
|
return {
|
|
400
401
|
type: 'expression',
|
|
401
402
|
value: expression,
|
|
@@ -421,32 +422,105 @@ const generateIndexes = async (client: Client, schemas: string[]) => {
|
|
|
421
422
|
return acc;
|
|
422
423
|
}, {});
|
|
423
424
|
|
|
424
|
-
const tableConstraints
|
|
425
|
+
const tableConstraints: TableConstraintsDictionary = {};
|
|
426
|
+
|
|
427
|
+
inlineIndexes.forEach((row: Record<string, any>) => {
|
|
425
428
|
const {
|
|
426
429
|
table_schema,
|
|
427
430
|
table_name,
|
|
428
|
-
|
|
431
|
+
index_columns,
|
|
429
432
|
constraint_type,
|
|
430
433
|
} = row;
|
|
431
434
|
const key = `${table_schema}.${table_name}`;
|
|
432
|
-
if (!
|
|
435
|
+
if (!tableConstraints[key]) tableConstraints[key] = {};
|
|
433
436
|
|
|
434
|
-
const columnNames =
|
|
437
|
+
const columnNames = index_columns.split(',').map((column: string) => column.trim());
|
|
435
438
|
columnNames.forEach((columnName: string) => {
|
|
436
|
-
if (!
|
|
439
|
+
if (!tableConstraints[key][columnName]) tableConstraints[key][columnName] = { checks: [] };
|
|
437
440
|
if (constraint_type === 'PRIMARY KEY') {
|
|
438
|
-
|
|
441
|
+
tableConstraints[key][columnName].pk = true;
|
|
439
442
|
}
|
|
440
|
-
if (constraint_type === 'UNIQUE' && !
|
|
441
|
-
|
|
443
|
+
if (constraint_type === 'UNIQUE' && !tableConstraints[key][columnName].pk) {
|
|
444
|
+
tableConstraints[key][columnName].unique = true;
|
|
442
445
|
}
|
|
443
446
|
});
|
|
444
|
-
return acc;
|
|
445
447
|
}, {});
|
|
446
448
|
|
|
449
|
+
const checkListSql = `
|
|
450
|
+
SELECT
|
|
451
|
+
n.nspname AS table_schema,
|
|
452
|
+
c.relname AS table_name,
|
|
453
|
+
MAX(a.attname) AS column_name,
|
|
454
|
+
con.conname AS check_name,
|
|
455
|
+
pg_get_constraintdef(con.oid) AS check_definition,
|
|
456
|
+
CASE
|
|
457
|
+
WHEN COUNT(*) = 1 THEN TRUE
|
|
458
|
+
ELSE FALSE
|
|
459
|
+
END AS is_column_constraint
|
|
460
|
+
FROM
|
|
461
|
+
pg_catalog.pg_constraint AS con
|
|
462
|
+
JOIN
|
|
463
|
+
pg_catalog.pg_class AS c
|
|
464
|
+
ON con.conrelid = c.oid
|
|
465
|
+
LEFT JOIN
|
|
466
|
+
pg_catalog.pg_namespace AS n
|
|
467
|
+
ON c.relnamespace = n.oid
|
|
468
|
+
LEFT JOIN
|
|
469
|
+
pg_catalog.pg_attribute AS a
|
|
470
|
+
ON a.attrelid = c.oid AND a.attnum = ANY(con.conkey)
|
|
471
|
+
WHERE con.contype = 'c' -- 'c' for CHECK constraints
|
|
472
|
+
${buildSchemaQuery('n.nspname', schemas)}
|
|
473
|
+
GROUP BY
|
|
474
|
+
con.conname,
|
|
475
|
+
n.nspname,
|
|
476
|
+
c.relname,
|
|
477
|
+
con.oid
|
|
478
|
+
`;
|
|
479
|
+
const checkListResult = await client.query(checkListSql);
|
|
480
|
+
const checks: CheckConstraintDictionary = {};
|
|
481
|
+
checkListResult.rows.forEach((row) => {
|
|
482
|
+
const {
|
|
483
|
+
table_schema,
|
|
484
|
+
table_name,
|
|
485
|
+
check_name,
|
|
486
|
+
check_definition,
|
|
487
|
+
column_name,
|
|
488
|
+
is_column_constraint,
|
|
489
|
+
} = row;
|
|
490
|
+
if (typeof table_schema !== 'string' || typeof table_name !== 'string' || typeof check_definition !== 'string') {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
if (!check_definition.match(/CHECK \(\(.*\)\)/)) {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
if (is_column_constraint && typeof column_name !== 'string') {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
const key = `${table_schema}.${table_name}`;
|
|
500
|
+
if (is_column_constraint) {
|
|
501
|
+
if (!tableConstraints[key]) tableConstraints[key] = {};
|
|
502
|
+
if (!tableConstraints[key][column_name]) tableConstraints[key][column_name] = { checks: [] };
|
|
503
|
+
tableConstraints[key][column_name].checks.push({
|
|
504
|
+
name: check_name || undefined,
|
|
505
|
+
// The check definition has the form: `CHECK ((expr))`
|
|
506
|
+
// The expression starts at 8 and ends at -2
|
|
507
|
+
expression: check_definition.slice(8, -2),
|
|
508
|
+
});
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
if (!checks[key]) checks[key] = [];
|
|
512
|
+
checks[key].push({
|
|
513
|
+
name: check_name || undefined,
|
|
514
|
+
// The check definition has the form: `CHECK ((expr))`
|
|
515
|
+
// The expression starts at 8 and ends at -2
|
|
516
|
+
expression: check_definition.slice(8, -2),
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
|
|
447
520
|
return {
|
|
448
521
|
indexes,
|
|
449
522
|
tableConstraints,
|
|
523
|
+
checks,
|
|
450
524
|
};
|
|
451
525
|
};
|
|
452
526
|
|
|
@@ -496,7 +570,7 @@ const fetchSchemaJson = async (connection: string): Promise<DatabaseSchema> => {
|
|
|
496
570
|
const client = await getValidatedClient(connectionString);
|
|
497
571
|
|
|
498
572
|
const tablesAndFieldsRes = generateTablesAndFields(client, schemas);
|
|
499
|
-
const indexesRes =
|
|
573
|
+
const indexesRes = generateIndexesAndConstraints(client, schemas);
|
|
500
574
|
const refsRes = generateRawRefs(client, schemas);
|
|
501
575
|
const enumsRes = generateRawEnums(client, schemas);
|
|
502
576
|
|
|
@@ -509,7 +583,7 @@ const fetchSchemaJson = async (connection: string): Promise<DatabaseSchema> => {
|
|
|
509
583
|
client.end();
|
|
510
584
|
|
|
511
585
|
const { tables, fields } = res[0];
|
|
512
|
-
const { indexes, tableConstraints } = res[1];
|
|
586
|
+
const { indexes, tableConstraints, checks } = res[1];
|
|
513
587
|
const refs = res[2];
|
|
514
588
|
const enums = res[3];
|
|
515
589
|
|
|
@@ -520,9 +594,10 @@ const fetchSchemaJson = async (connection: string): Promise<DatabaseSchema> => {
|
|
|
520
594
|
enums,
|
|
521
595
|
indexes,
|
|
522
596
|
tableConstraints,
|
|
597
|
+
checks,
|
|
523
598
|
};
|
|
524
599
|
};
|
|
525
600
|
|
|
526
601
|
export {
|
|
527
|
-
fetchSchemaJson
|
|
602
|
+
fetchSchemaJson,
|
|
528
603
|
};
|
|
@@ -43,12 +43,11 @@ const parseConnectionString = (connectionString: string): Record<string, string>
|
|
|
43
43
|
let match;
|
|
44
44
|
|
|
45
45
|
while ((match = regex.exec(connectionString)) !== null) {
|
|
46
|
-
|
|
46
|
+
params[match[1].trim()] = match[2].trim();
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
return params;
|
|
50
|
-
}
|
|
51
|
-
|
|
50
|
+
};
|
|
52
51
|
|
|
53
52
|
const connect = async (connection: Connection): Promise<void> => {
|
|
54
53
|
return new Promise((resolve, reject) => {
|
|
@@ -61,13 +60,13 @@ const connect = async (connection: Connection): Promise<void> => {
|
|
|
61
60
|
}
|
|
62
61
|
});
|
|
63
62
|
});
|
|
64
|
-
}
|
|
63
|
+
};
|
|
65
64
|
|
|
66
65
|
const executeQuery = (connection: Connection, sqlText: string): Promise<any[]> => {
|
|
67
66
|
return new Promise((resolve, reject) => {
|
|
68
67
|
console.log('Executing query...');
|
|
69
68
|
connection.execute({
|
|
70
|
-
sqlText
|
|
69
|
+
sqlText,
|
|
71
70
|
complete: (err, stmt, rows) => {
|
|
72
71
|
if (err) {
|
|
73
72
|
reject(err);
|
|
@@ -77,14 +76,14 @@ const executeQuery = (connection: Connection, sqlText: string): Promise<any[]> =
|
|
|
77
76
|
resolve(rows);
|
|
78
77
|
}
|
|
79
78
|
}
|
|
80
|
-
}
|
|
79
|
+
},
|
|
81
80
|
});
|
|
82
81
|
});
|
|
83
|
-
}
|
|
82
|
+
};
|
|
84
83
|
|
|
85
84
|
const isLogLevel = (value: string): value is LogLevel => {
|
|
86
|
-
return ['ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE'].includes(value as LogLevel)
|
|
87
|
-
}
|
|
85
|
+
return ['ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE'].includes(value as LogLevel);
|
|
86
|
+
};
|
|
88
87
|
|
|
89
88
|
const connectToSnowflake = async (config: Record<string, string>): Promise<Connection> => {
|
|
90
89
|
const logLevel = isLogLevel(config.LOG_LEVEL) ? config.LOG_LEVEL : 'INFO';
|
|
@@ -266,14 +265,16 @@ const generateTablesAndFields = async (conn: Connection, schemas: string[]): Pro
|
|
|
266
265
|
|
|
267
266
|
const createConstraintKeysMap = (keys: Record<string, string>[], schemas: string[], constraintType: 'primary' | 'unique'): Record<string, ConstraintRow> => {
|
|
268
267
|
return keys.reduce((acc: Record<string, ConstraintRow>, row: Record<string, string>) => {
|
|
269
|
-
const {
|
|
268
|
+
const {
|
|
269
|
+
schema_name, table_name, column_name, constraint_name,
|
|
270
|
+
} = row;
|
|
270
271
|
const selectedSchema = schemas.length > 0 ? schemas.includes(schema_name) : true;
|
|
271
272
|
if (!selectedSchema) { return acc; }
|
|
272
273
|
|
|
273
274
|
const key = `${schema_name}.${table_name}.${constraint_name}`;
|
|
274
275
|
|
|
275
276
|
if (acc[key]) {
|
|
276
|
-
const columnNames = acc[key]
|
|
277
|
+
const { columnNames } = acc[key];
|
|
277
278
|
columnNames.push(column_name);
|
|
278
279
|
return acc;
|
|
279
280
|
}
|
|
@@ -291,8 +292,6 @@ const createConstraintKeysMap = (keys: Record<string, string>[], schemas: string
|
|
|
291
292
|
}, {});
|
|
292
293
|
};
|
|
293
294
|
const generateIndexes = async (conn: Connection, databaseName: string, schemas: string[]) => {
|
|
294
|
-
|
|
295
|
-
|
|
296
295
|
const getPrimaryKeysSql = `
|
|
297
296
|
SHOW PRIMARY KEYS IN DATABASE ${databaseName};
|
|
298
297
|
`;
|
|
@@ -309,7 +308,9 @@ const generateIndexes = async (conn: Connection, databaseName: string, schemas:
|
|
|
309
308
|
|
|
310
309
|
const allConstraints: ConstraintRow[] = [Object.values(primaryKeysByConstraint), Object.values(uniqueKeysByConstraint)].flat();
|
|
311
310
|
const { indexes, tableConstraints } = allConstraints.reduce((acc: GeneratedIndexes, row: ConstraintRow): GeneratedIndexes => {
|
|
312
|
-
const {
|
|
311
|
+
const {
|
|
312
|
+
schemaName, tableName, constraintName, columnNames, type, primary, unique,
|
|
313
|
+
} = row;
|
|
313
314
|
const key = `${schemaName}.${tableName}`;
|
|
314
315
|
|
|
315
316
|
if (columnNames.length < 2) {
|
|
@@ -317,6 +318,7 @@ const generateIndexes = async (conn: Connection, databaseName: string, schemas:
|
|
|
317
318
|
[columnNames[0]]: {
|
|
318
319
|
pk: primary,
|
|
319
320
|
unique,
|
|
321
|
+
checks: [],
|
|
320
322
|
},
|
|
321
323
|
};
|
|
322
324
|
return acc;
|
|
@@ -340,7 +342,7 @@ const generateIndexes = async (conn: Connection, databaseName: string, schemas:
|
|
|
340
342
|
}
|
|
341
343
|
|
|
342
344
|
return acc;
|
|
343
|
-
}, { indexes: {}, tableConstraints: {}});
|
|
345
|
+
}, { indexes: {}, tableConstraints: {} });
|
|
344
346
|
|
|
345
347
|
return {
|
|
346
348
|
indexes,
|
|
@@ -384,6 +386,8 @@ const fetchSchemaJson = async (connection: string): Promise<DatabaseSchema> => {
|
|
|
384
386
|
enums: [],
|
|
385
387
|
indexes,
|
|
386
388
|
tableConstraints,
|
|
389
|
+
// Checks are not supported in snowflake
|
|
390
|
+
checks: {},
|
|
387
391
|
};
|
|
388
392
|
};
|
|
389
393
|
|
package/src/connectors/types.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { columns } from "mssql";
|
|
2
|
-
|
|
3
1
|
interface NoteInfo {
|
|
4
2
|
value: string;
|
|
5
3
|
}
|
|
@@ -9,7 +7,6 @@ interface TypeInfo {
|
|
|
9
7
|
schemaName: string | null;
|
|
10
8
|
}
|
|
11
9
|
|
|
12
|
-
|
|
13
10
|
type DefaultType = 'string' | 'number' | 'boolean' | 'expression';
|
|
14
11
|
|
|
15
12
|
interface DefaultInfo {
|
|
@@ -38,12 +35,22 @@ interface FieldsDictionary {
|
|
|
38
35
|
[key: string]: Field[]; // Represents fields within tables, indexed by schemaName.tableName
|
|
39
36
|
}
|
|
40
37
|
|
|
38
|
+
interface CheckConstraint {
|
|
39
|
+
name?: string;
|
|
40
|
+
expression: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface CheckConstraintDictionary {
|
|
44
|
+
[tableName: string]: CheckConstraint[];
|
|
45
|
+
}
|
|
46
|
+
|
|
41
47
|
interface TableConstraint {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
[fieldName: string]: {
|
|
49
|
+
pk?: boolean;
|
|
50
|
+
unique?: boolean;
|
|
51
|
+
checks: CheckConstraint[];
|
|
45
52
|
};
|
|
46
|
-
}
|
|
53
|
+
}
|
|
47
54
|
|
|
48
55
|
interface TableConstraintsDictionary {
|
|
49
56
|
[tableIdentifier: string]: TableConstraint; // Represents constraints within tables, indexed by schemaName.tableName
|
|
@@ -97,6 +104,7 @@ interface DatabaseSchema {
|
|
|
97
104
|
tableConstraints: TableConstraintsDictionary;
|
|
98
105
|
refs: Ref[];
|
|
99
106
|
indexes: IndexesDictionary; // Represents the indexes property
|
|
107
|
+
checks: CheckConstraintDictionary;
|
|
100
108
|
}
|
|
101
109
|
|
|
102
110
|
interface BigQueryCredentials {
|
|
@@ -108,59 +116,6 @@ interface BigQueryCredentials {
|
|
|
108
116
|
datasets: string[],
|
|
109
117
|
}
|
|
110
118
|
|
|
111
|
-
// Currently, we parse the check constraints as enum values in mssql.
|
|
112
|
-
// However, one check constraint can have multiple set of enum values.
|
|
113
|
-
// So, we need to store the enum values in a dictionary.
|
|
114
|
-
// Examples:
|
|
115
|
-
// CREATE TABLE AddressInfo (
|
|
116
|
-
// A1 UNIQUEIDENTIFIER NOT NULL,
|
|
117
|
-
// A2 UNIQUEIDENTIFIER NOT NULL,
|
|
118
|
-
// A3 UNIQUEIDENTIFIER NOT NULL,
|
|
119
|
-
// A4 UNIQUEIDENTIFIER NOT NULL,
|
|
120
|
-
// CONSTRAINT CK_Address_Valid CHECK (
|
|
121
|
-
// A1 IN ('1111', '2222', '333') AND
|
|
122
|
-
// A2 IN ('1111', '2222') AND
|
|
123
|
-
// A3 IN ('2222', '1111') AND
|
|
124
|
-
// A4 IN ('2222', '3333')
|
|
125
|
-
// )
|
|
126
|
-
// );
|
|
127
|
-
// GO
|
|
128
|
-
// In the above example, we have a check constraint CK_Address_Valid2 with 4 columns: A1, A2, A3, A4.
|
|
129
|
-
// Each column has a different set of enum values. So, we need to store the enum values in a dictionary.
|
|
130
|
-
// In the above example, the enum values dictionary will look like this:
|
|
131
|
-
// [
|
|
132
|
-
// {
|
|
133
|
-
// columns : ['A1'],
|
|
134
|
-
// enumValues: [
|
|
135
|
-
// { name: '1111' },
|
|
136
|
-
// { name: '2222' },
|
|
137
|
-
// { name: '333' }
|
|
138
|
-
// ],
|
|
139
|
-
// constraint_name: 'CK_Address_Valid_A1'
|
|
140
|
-
// },
|
|
141
|
-
// {
|
|
142
|
-
// columns : ['A2, A3'], => In this case, these two columns have the same set of enum values.
|
|
143
|
-
// enumValues: [
|
|
144
|
-
// { name: '1111' },
|
|
145
|
-
// { name: '2222' }
|
|
146
|
-
// ],
|
|
147
|
-
// constraint_name: 'CK_Address_Valid_A2_A3'
|
|
148
|
-
// },
|
|
149
|
-
// {
|
|
150
|
-
// columns : ['A4'],
|
|
151
|
-
// enumValues: [
|
|
152
|
-
// { name: '2222' },
|
|
153
|
-
// { name: '3333' }
|
|
154
|
-
// ],
|
|
155
|
-
// constraint_name: 'CK_Address_Valid_A4'
|
|
156
|
-
// }
|
|
157
|
-
// ]
|
|
158
|
-
interface EnumValuesDict {
|
|
159
|
-
columns: string[],
|
|
160
|
-
enumValues: EnumValue[],
|
|
161
|
-
constraint_name: string,
|
|
162
|
-
}
|
|
163
|
-
|
|
164
119
|
export {
|
|
165
120
|
NoteInfo,
|
|
166
121
|
TypeInfo,
|
|
@@ -180,5 +135,5 @@ export {
|
|
|
180
135
|
IndexesDictionary,
|
|
181
136
|
DatabaseSchema,
|
|
182
137
|
BigQueryCredentials,
|
|
183
|
-
|
|
138
|
+
CheckConstraintDictionary,
|
|
184
139
|
};
|