@dbml/connector 5.0.0-alpha.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.
@@ -5,6 +5,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.fetchSchemaJson = void 0;
7
7
  /* eslint-disable camelcase */
8
+ /* TDD 72
9
+ * - In MSSQL, there's no notion of ENUM.
10
+ * - MSSQL connector used to extract ENUMS from CHECK constraints, for example: `CHECK ([col]=value1 OR [col]=value2)` would be translated to an enum with 2 values, `value1` and `value2`.
11
+ * - Since DBML 4.0.0, we have supported CHECK constraints, so we no longer extract ENUMS out from CHECK constraints but rather keep them as CHECK constraints.
12
+ * */
8
13
  const mssql_1 = __importDefault(require("mssql"));
9
14
  const parseSchema_1 = require("../utils/parseSchema");
10
15
  // https://learn.microsoft.com/en-us/sql/t-sql/data-types/date-and-time-types?view=sql-server-ver15
@@ -88,63 +93,6 @@ const getDbdefault = (data_type, column_default, default_type) => {
88
93
  value: default_type === 'string' ? value.slice(1, -1) : value, // Remove the quotes for string values
89
94
  };
90
95
  };
91
- const getEnumValues = (definition, constraint_name) => {
92
- // Use the example below to understand the regex:
93
- // ([quantity]>(0))
94
- // ([unit_price]>(0))
95
- // ([status]='cancelled' OR [status]='delivered' OR [status]='shipped' OR [status]='processing' OR [status]='pending')
96
- // ([total_amount]>(0))
97
- // ([price]>(0))
98
- // ([stock_quantity]>=(0))
99
- // ([age_start]<=[age_end])
100
- // ([age_start]<=[age_end])
101
- // ([gender]='Other' OR [gender]='Female' OR [gender]='Male')
102
- // ([date_of_birth]<=dateadd(year,(-13),getdate()))
103
- // ([email] like '%_@_%._%')
104
- if (!definition)
105
- return [];
106
- const values = definition.match(/\[([^\]]+)\]='([^']+)'/g); // Extracting the enum values when the definition contains ]='
107
- if (!values)
108
- return [];
109
- const colMap = {};
110
- values.forEach((value) => {
111
- const [columnName, enumValue] = value.split("]='");
112
- const cleanColumnName = columnName.slice(1); // Remove the leading '['
113
- const cleanEnumValue = enumValue.slice(0, -1); // Remove the trailing "'"
114
- if (!colMap[cleanColumnName]) {
115
- colMap[cleanColumnName] = [cleanEnumValue];
116
- }
117
- else {
118
- colMap[cleanColumnName].push(cleanEnumValue);
119
- }
120
- });
121
- const arraysEqual = (a, b) => {
122
- if (a.length !== b.length)
123
- return false;
124
- const sortedA = [...a].sort();
125
- const sortedB = [...b].sort();
126
- return sortedA.every((value, index) => value === sortedB[index]);
127
- };
128
- // Please check the comments of the EnumValuesDict type in types.ts to understand the structure
129
- const result = [];
130
- const processedKeys = new Set();
131
- Object.keys(colMap).forEach((key) => {
132
- if (processedKeys.has(key))
133
- return;
134
- const mergedColumns = [key];
135
- const colValues = colMap[key];
136
- Object.keys(colMap).forEach((innerKey) => {
137
- if (key !== innerKey && arraysEqual(colValues, colMap[innerKey])) {
138
- mergedColumns.push(innerKey);
139
- processedKeys.add(innerKey);
140
- }
141
- });
142
- const enumValues = colValues.map((value) => ({ name: value }));
143
- result.push({ columns: mergedColumns, enumValues, constraint_name: `${constraint_name}_${mergedColumns.join('_')}` });
144
- processedKeys.add(key);
145
- });
146
- return result;
147
- };
148
96
  const generateField = (row) => {
149
97
  const { column_name, data_type, character_maximum_length, numeric_precision, numeric_scale, identity_increment, is_nullable, column_default, default_type, column_comment, } = row;
150
98
  const dbdefault = column_default && default_type !== 'increment' ? getDbdefault(data_type, column_default, default_type) : null;
@@ -161,10 +109,8 @@ const generateField = (row) => {
161
109
  note: column_comment ? { value: column_comment } : { value: '' },
162
110
  };
163
111
  };
164
- const generateTablesFieldsAndEnums = async (client, schemas) => {
165
- const checkConstraintDefinitionDedup = {};
112
+ const generateTablesFields = async (client, schemas) => {
166
113
  const fields = {};
167
- const enums = [];
168
114
  const tablesAndFieldsSql = `
169
115
  WITH tables_and_fields AS (
170
116
  SELECT
@@ -203,35 +149,6 @@ const generateTablesFieldsAndEnums = async (client, schemas) => {
203
149
  AND ep.name like '%description%'
204
150
  WHERE
205
151
  t.type = 'U' -- User-defined tables
206
- ),
207
- constraints_with_nulls AS (
208
- SELECT
209
- tf.*,
210
- cc.name AS check_constraint_name,
211
- CASE
212
- WHEN cc.definition LIKE '%[' + tf.column_name + ']%=''%' THEN cc.definition
213
- ELSE NULL
214
- END AS check_constraint_definition
215
- FROM
216
- tables_and_fields AS tf
217
- LEFT JOIN sys.check_constraints cc
218
- ON cc.parent_object_id = OBJECT_ID(tf.table_schema + '.' + tf.table_name)
219
- AND cc.definition LIKE '%' + tf.column_name + '%' -- Ensure the constraint references the column
220
- ),
221
- constraints_with_row_number AS (
222
- SELECT
223
- *,
224
- ROW_NUMBER() OVER (
225
- PARTITION BY table_schema, table_name, column_name
226
- ORDER BY
227
- CASE
228
- WHEN check_constraint_definition IS NOT NULL THEN 1
229
- ELSE 2
230
- END,
231
- check_constraint_name
232
- ) AS rn
233
- FROM
234
- constraints_with_nulls
235
152
  )
236
153
  SELECT
237
154
  table_schema,
@@ -246,17 +163,15 @@ const generateTablesFieldsAndEnums = async (client, schemas) => {
246
163
  column_default,
247
164
  table_comment,
248
165
  column_comment,
249
- check_constraint_name,
250
- check_constraint_definition,
251
166
  CASE
252
167
  WHEN column_default LIKE '((%))' THEN 'number'
253
168
  WHEN column_default LIKE '(''%'')' THEN 'string'
254
169
  ELSE 'expression'
255
170
  END AS default_type
256
171
  FROM
257
- constraints_with_row_number
172
+ tables_and_fields
258
173
  WHERE
259
- rn = 1
174
+ 1 = 1
260
175
  ${(0, parseSchema_1.buildSchemaQuery)('table_schema', schemas)}
261
176
  ORDER BY
262
177
  table_schema,
@@ -266,7 +181,7 @@ const generateTablesFieldsAndEnums = async (client, schemas) => {
266
181
  `;
267
182
  const tablesAndFieldsResult = await client.query(tablesAndFieldsSql);
268
183
  const tables = tablesAndFieldsResult.recordset.reduce((acc, row) => {
269
- const { table_schema, table_name, table_comment, check_constraint_name, check_constraint_definition, } = row;
184
+ const { table_schema, table_name, table_comment, } = row;
270
185
  const key = `${table_schema}.${table_name}`;
271
186
  if (!acc[key]) {
272
187
  acc[key] = {
@@ -278,34 +193,12 @@ const generateTablesFieldsAndEnums = async (client, schemas) => {
278
193
  if (!fields[key])
279
194
  fields[key] = [];
280
195
  const field = generateField(row);
281
- if (check_constraint_definition && !checkConstraintDefinitionDedup[check_constraint_name]) {
282
- const enumValuesByColumns = getEnumValues(check_constraint_definition, check_constraint_name);
283
- enumValuesByColumns.forEach((item) => {
284
- enums.push({
285
- name: item.constraint_name,
286
- schemaName: table_schema,
287
- values: item.enumValues,
288
- });
289
- });
290
- checkConstraintDefinitionDedup[check_constraint_name] = enumValuesByColumns;
291
- }
292
- if (checkConstraintDefinitionDedup[check_constraint_name]) {
293
- const enumValuesByColumns = checkConstraintDefinitionDedup[check_constraint_name];
294
- const columnEnumValues = enumValuesByColumns.find((item) => item.columns.includes(field.name));
295
- if (columnEnumValues) {
296
- field.type = {
297
- type_name: columnEnumValues.constraint_name,
298
- schemaName: table_schema,
299
- };
300
- }
301
- }
302
196
  fields[key].push(field);
303
197
  return acc;
304
198
  }, {});
305
199
  return {
306
200
  tables: Object.values(tables),
307
201
  fields,
308
- enums,
309
202
  };
310
203
  };
311
204
  const generateRefs = async (client, schemas) => {
@@ -379,7 +272,7 @@ const generateRefs = async (client, schemas) => {
379
272
  });
380
273
  return refs;
381
274
  };
382
- const generateIndexes = async (client, schemas) => {
275
+ const generateIndexesAndConstraints = async (client, schemas) => {
383
276
  const indexListSql = `
384
277
  WITH user_tables AS (
385
278
  SELECT
@@ -455,19 +348,19 @@ const generateIndexes = async (client, schemas) => {
455
348
  ii.index_name;
456
349
  `;
457
350
  const indexListResult = await client.query(indexListSql);
458
- const { outOfLineConstraints, inlineConstraints } = indexListResult.recordset.reduce((acc, row) => {
351
+ const { outOfLineIndexes, inlineIndexes } = indexListResult.recordset.reduce((acc, row) => {
459
352
  const { constraint_type, columns } = row;
460
353
  if (columns === 'null' || columns.trim() === '')
461
354
  return acc;
462
355
  if (constraint_type === 'PRIMARY KEY' || constraint_type === 'UNIQUE') {
463
- acc.inlineConstraints.push(row);
356
+ acc.inlineIndexes.push(row);
464
357
  }
465
358
  else {
466
- acc.outOfLineConstraints.push(row);
359
+ acc.outOfLineIndexes.push(row);
467
360
  }
468
361
  return acc;
469
- }, { outOfLineConstraints: [], inlineConstraints: [] });
470
- const indexes = outOfLineConstraints.reduce((acc, indexRow) => {
362
+ }, { inlineIndexes: [], outOfLineIndexes: [] });
363
+ const indexes = outOfLineIndexes.reduce((acc, indexRow) => {
471
364
  const { table_schema, table_name, index_name, index_type, columns, expressions, } = indexRow;
472
365
  const indexColumns = columns.split(',').map((column) => {
473
366
  return {
@@ -498,51 +391,88 @@ const generateIndexes = async (client, schemas) => {
498
391
  }
499
392
  return acc;
500
393
  }, {});
501
- const tableConstraints = inlineConstraints.reduce((acc, row) => {
394
+ const tableConstraints = {};
395
+ inlineIndexes.forEach((row) => {
502
396
  const { table_schema, table_name, columns, constraint_type, } = row;
503
397
  const key = `${table_schema}.${table_name}`;
504
- if (!acc[key])
505
- acc[key] = {};
398
+ if (!tableConstraints[key])
399
+ tableConstraints[key] = {};
506
400
  const columnNames = columns.split(',').map((column) => column.trim());
507
401
  columnNames.forEach((columnName) => {
508
- if (!acc[key][columnName])
509
- acc[key][columnName] = {};
402
+ if (!tableConstraints[key][columnName])
403
+ tableConstraints[key][columnName] = { checks: [] };
510
404
  if (constraint_type === 'PRIMARY KEY') {
511
- acc[key][columnName].pk = true;
405
+ tableConstraints[key][columnName].pk = true;
512
406
  }
513
- if (constraint_type === 'UNIQUE' && !acc[key][columnName].pk) {
514
- acc[key][columnName].unique = true;
407
+ if (constraint_type === 'UNIQUE' && !tableConstraints[key][columnName].pk) {
408
+ tableConstraints[key][columnName].unique = true;
515
409
  }
516
410
  });
517
- return acc;
518
- }, {});
411
+ });
412
+ const checkListSql = `
413
+ SELECT
414
+ OBJECT_SCHEMA_NAME(cc.parent_object_id) AS table_schema,
415
+ OBJECT_NAME(cc.parent_object_id) AS table_name,
416
+ c.name AS column_name,
417
+ cc.name AS check_name,
418
+ cc.definition AS check_expression
419
+ FROM
420
+ sys.check_constraints cc
421
+ LEFT JOIN
422
+ sys.columns c ON cc.parent_object_id = c.object_id AND cc.parent_column_id = c.column_id
423
+ WHERE 1 = 1
424
+ ${(0, parseSchema_1.buildSchemaQuery)('OBJECT_SCHEMA_NAME(cc.parent_object_id)', schemas)}
425
+ `;
426
+ const checkListResult = await client.query(checkListSql);
427
+ const checks = {};
428
+ checkListResult.recordset.forEach((row) => {
429
+ const { table_schema, table_name, column_name, check_name, check_expression, } = row;
430
+ const key = `${table_schema}.${table_name}`;
431
+ if (column_name) {
432
+ if (!tableConstraints[key])
433
+ tableConstraints[key] = {};
434
+ if (!tableConstraints[key][column_name])
435
+ tableConstraints[key][column_name] = { checks: [] };
436
+ // The check definition has the form: `(expr)`
437
+ // The expression starts at 1 and ends at -1
438
+ tableConstraints[key][column_name].checks.push({ name: check_name, expression: check_expression.slice(1, -1) });
439
+ return;
440
+ }
441
+ if (!checks[key])
442
+ checks[key] = [];
443
+ // The check definition has the form: `(expr)`
444
+ // The expression starts at 1 and ends at -1
445
+ checks[key].push({ name: check_name, expression: check_expression.slice(1, -1) });
446
+ });
519
447
  return {
520
448
  indexes,
521
449
  tableConstraints,
450
+ checks,
522
451
  };
523
452
  };
524
453
  const fetchSchemaJson = async (connection) => {
525
454
  const { connectionString, schemas } = (0, parseSchema_1.parseConnectionString)(connection, 'odbc');
526
455
  const client = await getValidatedClient(connectionString);
527
- const tablesFieldsAndEnumsRes = generateTablesFieldsAndEnums(client, schemas);
528
- const indexesRes = generateIndexes(client, schemas);
456
+ const tablesRes = generateTablesFields(client, schemas);
457
+ const indexesAndConstraintsRes = generateIndexesAndConstraints(client, schemas);
529
458
  const refsRes = generateRefs(client, schemas);
530
459
  const res = await Promise.all([
531
- tablesFieldsAndEnumsRes,
532
- indexesRes,
460
+ tablesRes,
461
+ indexesAndConstraintsRes,
533
462
  refsRes,
534
463
  ]);
535
- client.close();
536
- const { tables, fields, enums } = res[0];
537
- const { indexes, tableConstraints } = res[1];
464
+ const { tables, fields } = res[0];
465
+ const { indexes, tableConstraints, checks } = res[1];
538
466
  const refs = res[2];
539
467
  return {
540
468
  tables,
541
469
  fields,
542
- enums,
470
+ // MSSQL doesn't support the notion of enums
471
+ enums: [],
543
472
  refs,
544
473
  indexes,
545
474
  tableConstraints,
475
+ checks,
546
476
  };
547
477
  };
548
478
  exports.fetchSchemaJson = fetchSchemaJson;
@@ -164,7 +164,7 @@ async function generateEnums(client, schemaName) {
164
164
  return enumList;
165
165
  }
166
166
  /**
167
- * Mysql is automatically create index for primary keys, foreign keys, unique constraint. -> Ignore
167
+ * Mysql automatically creates index for primary keys, foreign keys, unique constraint. -> Ignore
168
168
  */
169
169
  async function generateIndexes(client, schemaName) {
170
170
  const query = `
@@ -251,8 +251,8 @@ async function generatePrimaryAndUniqueConstraint(client, schemaName) {
251
251
  const queryResponse = await client.query(query, [schemaName]);
252
252
  const rows = queryResponse[0];
253
253
  const inlineConstraintList = rows.filter((constraint) => constraint.columnCount === 1);
254
- const outOfConstraintList = rows.filter((constraint) => constraint.columnCount > 1);
255
- const compositeTableConstraintMap = outOfConstraintList.reduce((acc, row) => {
254
+ const outOfLineConstraintList = rows.filter((constraint) => constraint.columnCount > 1);
255
+ const compositeTableConstraintMap = outOfLineConstraintList.reduce((acc, row) => {
256
256
  const { tableName, constraintName, columnNames, constraintType, } = row;
257
257
  const key = tableName;
258
258
  if (!acc[key]) {
@@ -288,7 +288,7 @@ async function generatePrimaryAndUniqueConstraint(client, schemaName) {
288
288
  const columnList = columnNames.split(',');
289
289
  columnList.forEach((columnName) => {
290
290
  if (!acc[key][columnName]) {
291
- acc[key][columnName] = {};
291
+ acc[key][columnName] = { checks: [] };
292
292
  }
293
293
  if (constraintType === 'PRIMARY KEY') {
294
294
  acc[key][columnName].pk = true;
@@ -299,7 +299,54 @@ async function generatePrimaryAndUniqueConstraint(client, schemaName) {
299
299
  });
300
300
  return acc;
301
301
  }, {});
302
- return { compositeConstraintMap, constraintMap };
302
+ return { compositeConstraintMap, primaryAndUniqueConstraintMap: constraintMap };
303
+ }
304
+ function extractColumnsFromCheckConstraint(checkClause) {
305
+ return [...checkClause.matchAll(/`(?:``|[^`])*`/g)].map((col) => col.toString().slice(1, -1));
306
+ }
307
+ async function generateCheckConstraints(client, schemaName) {
308
+ const query = `
309
+ select
310
+ cc.constraint_name as constraintName,
311
+ tc.table_name as tableName,
312
+ cc.check_clause as checkClause
313
+ from information_schema.check_constraints cc
314
+ join information_schema.table_constraints tc
315
+ on cc.constraint_name = tc.constraint_name
316
+ where cc.constraint_schema = ?
317
+ `;
318
+ const queryResponse = await client.query(query, [schemaName]);
319
+ const rows = queryResponse[0];
320
+ const checks = {};
321
+ const tableConstraints = {};
322
+ rows.forEach((row) => {
323
+ const { constraintName, tableName, checkClause, } = row;
324
+ const key = tableName;
325
+ const cols = extractColumnsFromCheckConstraint(checkClause);
326
+ if (cols.length === 1) {
327
+ const col = cols[0];
328
+ if (!tableConstraints[key])
329
+ tableConstraints[key] = {};
330
+ if (!tableConstraints[key][col])
331
+ tableConstraints[key][col] = { checks: [] };
332
+ tableConstraints[key][col].checks.push({
333
+ name: constraintName,
334
+ // The check definition has the form: `(expr)`
335
+ // The expression starts at 1 and ends at -1
336
+ expression: checkClause.slice(1, -1),
337
+ });
338
+ return;
339
+ }
340
+ if (!checks[key])
341
+ checks[key] = [];
342
+ checks[key].push({
343
+ name: constraintName,
344
+ // The check definition has the form: `(expr)`
345
+ // The expression starts at 1 and ends at -1
346
+ expression: checkClause.slice(1, -1),
347
+ });
348
+ });
349
+ return { checks, tableConstraints };
303
350
  }
304
351
  async function generateForeignKeys(client, schemaName) {
305
352
  const query = `
@@ -375,17 +422,29 @@ async function fetchSchemaJson(connection) {
375
422
  generateIndexes(client, schemaName),
376
423
  generatePrimaryAndUniqueConstraint(client, schemaName),
377
424
  generateForeignKeys(client, schemaName),
425
+ generateCheckConstraints(client, schemaName),
378
426
  ]);
379
427
  client.end();
380
- const [{ tableList, fieldMap }, enumList, rawIndexMap, { constraintMap, compositeConstraintMap }, foreignKeyList,] = result;
428
+ const [{ tableList, fieldMap }, enumList, rawIndexMap, { primaryAndUniqueConstraintMap, compositeConstraintMap }, foreignKeyList, { checks: tableCheckConstraintMap, tableConstraints: columnCheckConstraintMap },] = result;
381
429
  // combine normal index and composite key
382
430
  const indexMap = combineIndexAndCompositeConstraint(rawIndexMap, compositeConstraintMap);
431
+ const tableConstraints = { ...primaryAndUniqueConstraintMap };
432
+ Object.keys(columnCheckConstraintMap).forEach((tableName) => {
433
+ if (!tableConstraints[tableName])
434
+ tableConstraints[tableName] = {};
435
+ Object.keys(columnCheckConstraintMap[tableName]).forEach((colName) => {
436
+ if (!tableConstraints[tableName][colName])
437
+ tableConstraints[tableName][colName] = { checks: [] };
438
+ tableConstraints[tableName][colName].checks.push(...columnCheckConstraintMap[tableName][colName].checks);
439
+ });
440
+ });
383
441
  return {
384
442
  tables: tableList,
385
443
  fields: fieldMap,
386
444
  refs: foreignKeyList,
387
445
  enums: enumList,
388
446
  indexes: indexMap,
389
- tableConstraints: constraintMap,
447
+ tableConstraints,
448
+ checks: tableCheckConstraintMap,
390
449
  };
391
450
  }
@@ -1,3 +1,3 @@
1
1
  import { DatabaseSchema } from './types';
2
2
  declare const fetchSchemaJson: (connection: string) => Promise<DatabaseSchema>;
3
- export { fetchSchemaJson };
3
+ export { fetchSchemaJson, };
@@ -241,7 +241,7 @@ const generateRawRefs = async (client, schemas) => {
241
241
  });
242
242
  return refs;
243
243
  };
244
- const generateIndexes = async (client, schemas) => {
244
+ const generateIndexesAndConstraints = async (client, schemas) => {
245
245
  const indexListSql = `
246
246
  WITH user_tables AS (
247
247
  SELECT
@@ -288,8 +288,8 @@ const generateIndexes = async (client, schemas) => {
288
288
  ii.is_unique,
289
289
  ii.is_primary,
290
290
  ii.index_type,
291
- ii.columns,
292
- ii.expressions,
291
+ ii.columns AS index_columns,
292
+ ii.expressions AS index_expressions,
293
293
  ii.constraint_type -- Retained constraint type here
294
294
  FROM
295
295
  user_tables ut
@@ -304,29 +304,29 @@ const generateIndexes = async (client, schemas) => {
304
304
  ;
305
305
  `;
306
306
  const indexListResult = await client.query(indexListSql);
307
- const { outOfLineConstraints, inlineConstraints } = indexListResult.rows.reduce((acc, row) => {
308
- const { constraint_type, columns } = row;
309
- if (columns === 'null' || columns.trim() === '')
307
+ const { outOfLineIndexes, inlineIndexes } = indexListResult.rows.reduce((acc, row) => {
308
+ const { constraint_type, index_columns } = row;
309
+ if (index_columns === 'null' || index_columns.trim() === '')
310
310
  return acc;
311
- const isSingleColumn = columns.split(',').length === 1;
312
- const isInlineConstraint = isSingleColumn && (constraint_type === 'PRIMARY KEY' || constraint_type === 'UNIQUE');
313
- if (isInlineConstraint) {
314
- acc.inlineConstraints.push(row);
311
+ const isSingleColumn = index_columns.split(',').length === 1;
312
+ const isInlineIndex = isSingleColumn && (constraint_type === 'PRIMARY KEY' || constraint_type === 'UNIQUE');
313
+ if (isInlineIndex) {
314
+ acc.inlineIndexes.push(row);
315
315
  }
316
316
  else {
317
- acc.outOfLineConstraints.push(row);
317
+ acc.outOfLineIndexes.push(row);
318
318
  }
319
319
  return acc;
320
- }, { outOfLineConstraints: [], inlineConstraints: [] });
321
- const indexes = outOfLineConstraints.reduce((acc, indexRow) => {
322
- const { table_schema, table_name, index_name, index_type, columns, expressions, } = indexRow;
323
- const indexColumns = columns.split(',').map((column) => {
320
+ }, { outOfLineIndexes: [], inlineIndexes: [] });
321
+ const indexes = outOfLineIndexes.reduce((acc, indexRow) => {
322
+ const { table_schema, table_name, index_name, index_type, index_columns, index_expressions, } = indexRow;
323
+ const indexColumns = index_columns.split(',').map((column) => {
324
324
  return {
325
325
  type: 'column',
326
326
  value: column.trim(),
327
327
  };
328
328
  });
329
- const indexExpressions = expressions ? expressions.split(',').map((expression) => {
329
+ const indexExpressions = index_expressions ? index_expressions.split(',').map((expression) => {
330
330
  return {
331
331
  type: 'expression',
332
332
  value: expression,
@@ -349,27 +349,94 @@ const generateIndexes = async (client, schemas) => {
349
349
  }
350
350
  return acc;
351
351
  }, {});
352
- const tableConstraints = inlineConstraints.reduce((acc, row) => {
353
- const { table_schema, table_name, columns, constraint_type, } = row;
352
+ const tableConstraints = {};
353
+ inlineIndexes.forEach((row) => {
354
+ const { table_schema, table_name, index_columns, constraint_type, } = row;
354
355
  const key = `${table_schema}.${table_name}`;
355
- if (!acc[key])
356
- acc[key] = {};
357
- const columnNames = columns.split(',').map((column) => column.trim());
356
+ if (!tableConstraints[key])
357
+ tableConstraints[key] = {};
358
+ const columnNames = index_columns.split(',').map((column) => column.trim());
358
359
  columnNames.forEach((columnName) => {
359
- if (!acc[key][columnName])
360
- acc[key][columnName] = {};
360
+ if (!tableConstraints[key][columnName])
361
+ tableConstraints[key][columnName] = { checks: [] };
361
362
  if (constraint_type === 'PRIMARY KEY') {
362
- acc[key][columnName].pk = true;
363
+ tableConstraints[key][columnName].pk = true;
363
364
  }
364
- if (constraint_type === 'UNIQUE' && !acc[key][columnName].pk) {
365
- acc[key][columnName].unique = true;
365
+ if (constraint_type === 'UNIQUE' && !tableConstraints[key][columnName].pk) {
366
+ tableConstraints[key][columnName].unique = true;
366
367
  }
367
368
  });
368
- return acc;
369
369
  }, {});
370
+ const checkListSql = `
371
+ SELECT
372
+ n.nspname AS table_schema,
373
+ c.relname AS table_name,
374
+ MAX(a.attname) AS column_name,
375
+ con.conname AS check_name,
376
+ pg_get_constraintdef(con.oid) AS check_definition,
377
+ CASE
378
+ WHEN COUNT(*) = 1 THEN TRUE
379
+ ELSE FALSE
380
+ END AS is_column_constraint
381
+ FROM
382
+ pg_catalog.pg_constraint AS con
383
+ JOIN
384
+ pg_catalog.pg_class AS c
385
+ ON con.conrelid = c.oid
386
+ LEFT JOIN
387
+ pg_catalog.pg_namespace AS n
388
+ ON c.relnamespace = n.oid
389
+ LEFT JOIN
390
+ pg_catalog.pg_attribute AS a
391
+ ON a.attrelid = c.oid AND a.attnum = ANY(con.conkey)
392
+ WHERE con.contype = 'c' -- 'c' for CHECK constraints
393
+ ${(0, parseSchema_1.buildSchemaQuery)('n.nspname', schemas)}
394
+ GROUP BY
395
+ con.conname,
396
+ n.nspname,
397
+ c.relname,
398
+ con.oid
399
+ `;
400
+ const checkListResult = await client.query(checkListSql);
401
+ const checks = {};
402
+ checkListResult.rows.forEach((row) => {
403
+ const { table_schema, table_name, check_name, check_definition, column_name, is_column_constraint, } = row;
404
+ if (typeof table_schema !== 'string' || typeof table_name !== 'string' || typeof check_definition !== 'string') {
405
+ return;
406
+ }
407
+ if (!check_definition.match(/CHECK \(\(.*\)\)/)) {
408
+ return;
409
+ }
410
+ if (is_column_constraint && typeof column_name !== 'string') {
411
+ return;
412
+ }
413
+ const key = `${table_schema}.${table_name}`;
414
+ if (is_column_constraint) {
415
+ if (!tableConstraints[key])
416
+ tableConstraints[key] = {};
417
+ if (!tableConstraints[key][column_name])
418
+ tableConstraints[key][column_name] = { checks: [] };
419
+ tableConstraints[key][column_name].checks.push({
420
+ name: check_name || undefined,
421
+ // The check definition has the form: `CHECK ((expr))`
422
+ // The expression starts at 8 and ends at -2
423
+ expression: check_definition.slice(8, -2),
424
+ });
425
+ return;
426
+ }
427
+ if (!checks[key])
428
+ checks[key] = [];
429
+ checks[key].push({
430
+ name: check_name || undefined,
431
+ // The check definition has the form: `CHECK ((expr))`
432
+ // The expression starts at 8 and ends at -2
433
+ expression: check_definition.slice(8, -2),
434
+ });
435
+ });
370
436
  return {
371
437
  indexes,
372
438
  tableConstraints,
439
+ checks,
373
440
  };
374
441
  };
375
442
  const generateRawEnums = async (client, schemas) => {
@@ -414,7 +481,7 @@ const fetchSchemaJson = async (connection) => {
414
481
  const { connectionString, schemas } = (0, parseSchema_1.parseConnectionString)(connection, 'jdbc');
415
482
  const client = await getValidatedClient(connectionString);
416
483
  const tablesAndFieldsRes = generateTablesAndFields(client, schemas);
417
- const indexesRes = generateIndexes(client, schemas);
484
+ const indexesRes = generateIndexesAndConstraints(client, schemas);
418
485
  const refsRes = generateRawRefs(client, schemas);
419
486
  const enumsRes = generateRawEnums(client, schemas);
420
487
  const res = await Promise.all([
@@ -425,7 +492,7 @@ const fetchSchemaJson = async (connection) => {
425
492
  ]);
426
493
  client.end();
427
494
  const { tables, fields } = res[0];
428
- const { indexes, tableConstraints } = res[1];
495
+ const { indexes, tableConstraints, checks } = res[1];
429
496
  const refs = res[2];
430
497
  const enums = res[3];
431
498
  return {
@@ -435,6 +502,7 @@ const fetchSchemaJson = async (connection) => {
435
502
  enums,
436
503
  indexes,
437
504
  tableConstraints,
505
+ checks,
438
506
  };
439
507
  };
440
508
  exports.fetchSchemaJson = fetchSchemaJson;