relq 1.0.5 → 1.0.6
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/dist/cjs/cli/commands/add.cjs +252 -12
- package/dist/cjs/cli/commands/commit.cjs +12 -1
- package/dist/cjs/cli/commands/export.cjs +25 -19
- package/dist/cjs/cli/commands/import.cjs +219 -100
- package/dist/cjs/cli/commands/init.cjs +86 -14
- package/dist/cjs/cli/commands/pull.cjs +104 -23
- package/dist/cjs/cli/commands/push.cjs +38 -3
- package/dist/cjs/cli/index.cjs +9 -1
- package/dist/cjs/cli/utils/ast/codegen/builder.cjs +297 -0
- package/dist/cjs/cli/utils/ast/codegen/constraints.cjs +185 -0
- package/dist/cjs/cli/utils/ast/codegen/defaults.cjs +311 -0
- package/dist/cjs/cli/utils/ast/codegen/index.cjs +24 -0
- package/dist/cjs/cli/utils/ast/codegen/type-map.cjs +116 -0
- package/dist/cjs/cli/utils/ast/codegen/utils.cjs +69 -0
- package/dist/cjs/cli/utils/ast/index.cjs +19 -0
- package/dist/cjs/cli/utils/ast/transformer/helpers.cjs +154 -0
- package/dist/cjs/cli/utils/ast/transformer/index.cjs +25 -0
- package/dist/cjs/cli/utils/ast/types.cjs +2 -0
- package/dist/cjs/cli/utils/ast-codegen.cjs +949 -0
- package/dist/cjs/cli/utils/ast-transformer.cjs +916 -0
- package/dist/cjs/cli/utils/change-tracker.cjs +50 -1
- package/dist/cjs/cli/utils/cli-utils.cjs +151 -0
- package/dist/cjs/cli/utils/fast-introspect.cjs +149 -23
- package/dist/cjs/cli/utils/pg-parser.cjs +1 -0
- package/dist/cjs/cli/utils/repo-manager.cjs +121 -4
- package/dist/cjs/cli/utils/schema-comparator.cjs +98 -14
- package/dist/cjs/cli/utils/schema-introspect.cjs +56 -19
- package/dist/cjs/cli/utils/snapshot-manager.cjs +0 -1
- package/dist/cjs/cli/utils/sql-generator.cjs +353 -64
- package/dist/cjs/cli/utils/type-generator.cjs +114 -15
- package/dist/cjs/core/relq-client.cjs +22 -6
- package/dist/cjs/schema-definition/column-types.cjs +149 -13
- package/dist/cjs/schema-definition/defaults.cjs +72 -0
- package/dist/cjs/schema-definition/index.cjs +15 -1
- package/dist/cjs/schema-definition/introspection.cjs +7 -3
- package/dist/cjs/schema-definition/pg-relations.cjs +169 -0
- package/dist/cjs/schema-definition/pg-view.cjs +30 -0
- package/dist/cjs/schema-definition/table-definition.cjs +110 -4
- package/dist/cjs/types/config-types.cjs +13 -4
- package/dist/cjs/utils/aws-dsql.cjs +177 -0
- package/dist/config.d.ts +146 -1
- package/dist/esm/cli/commands/add.js +250 -13
- package/dist/esm/cli/commands/commit.js +12 -1
- package/dist/esm/cli/commands/export.js +25 -19
- package/dist/esm/cli/commands/import.js +221 -102
- package/dist/esm/cli/commands/init.js +86 -14
- package/dist/esm/cli/commands/pull.js +106 -25
- package/dist/esm/cli/commands/push.js +39 -4
- package/dist/esm/cli/index.js +9 -1
- package/dist/esm/cli/utils/ast/codegen/builder.js +291 -0
- package/dist/esm/cli/utils/ast/codegen/constraints.js +176 -0
- package/dist/esm/cli/utils/ast/codegen/defaults.js +305 -0
- package/dist/esm/cli/utils/ast/codegen/index.js +6 -0
- package/dist/esm/cli/utils/ast/codegen/type-map.js +111 -0
- package/dist/esm/cli/utils/ast/codegen/utils.js +60 -0
- package/dist/esm/cli/utils/ast/index.js +3 -0
- package/dist/esm/cli/utils/ast/transformer/helpers.js +141 -0
- package/dist/esm/cli/utils/ast/transformer/index.js +2 -0
- package/dist/esm/cli/utils/ast/types.js +1 -0
- package/dist/esm/cli/utils/ast-codegen.js +945 -0
- package/dist/esm/cli/utils/ast-transformer.js +907 -0
- package/dist/esm/cli/utils/change-tracker.js +50 -1
- package/dist/esm/cli/utils/cli-utils.js +147 -0
- package/dist/esm/cli/utils/fast-introspect.js +149 -23
- package/dist/esm/cli/utils/pg-parser.js +1 -0
- package/dist/esm/cli/utils/repo-manager.js +114 -4
- package/dist/esm/cli/utils/schema-comparator.js +98 -14
- package/dist/esm/cli/utils/schema-introspect.js +56 -19
- package/dist/esm/cli/utils/snapshot-manager.js +0 -1
- package/dist/esm/cli/utils/sql-generator.js +353 -64
- package/dist/esm/cli/utils/type-generator.js +114 -15
- package/dist/esm/core/relq-client.js +23 -7
- package/dist/esm/schema-definition/column-types.js +146 -12
- package/dist/esm/schema-definition/defaults.js +69 -0
- package/dist/esm/schema-definition/index.js +3 -0
- package/dist/esm/schema-definition/introspection.js +7 -3
- package/dist/esm/schema-definition/pg-relations.js +161 -0
- package/dist/esm/schema-definition/pg-view.js +24 -0
- package/dist/esm/schema-definition/table-definition.js +110 -4
- package/dist/esm/types/config-types.js +12 -4
- package/dist/esm/utils/aws-dsql.js +139 -0
- package/dist/index.d.ts +159 -1
- package/dist/schema-builder.d.ts +1314 -32
- package/package.json +1 -1
|
@@ -5,6 +5,8 @@ const EXCLUDED_FUNCTIONS = [
|
|
|
5
5
|
'encrypt_iv'
|
|
6
6
|
];
|
|
7
7
|
function toCamelCase(str) {
|
|
8
|
+
if (!str)
|
|
9
|
+
return 'unknown';
|
|
8
10
|
const cleaned = str.replace(/^[_0-9]+/, '');
|
|
9
11
|
return cleaned
|
|
10
12
|
.split('_')
|
|
@@ -12,6 +14,8 @@ function toCamelCase(str) {
|
|
|
12
14
|
.join('');
|
|
13
15
|
}
|
|
14
16
|
function toPascalCase(str) {
|
|
17
|
+
if (!str)
|
|
18
|
+
return 'Unknown';
|
|
15
19
|
const cleaned = str.replace(/^[_0-9]+/, '');
|
|
16
20
|
return cleaned
|
|
17
21
|
.split('_')
|
|
@@ -19,6 +23,8 @@ function toPascalCase(str) {
|
|
|
19
23
|
.join('');
|
|
20
24
|
}
|
|
21
25
|
function inferTsTypeFromSqlType(sqlType) {
|
|
26
|
+
if (!sqlType)
|
|
27
|
+
return 'string';
|
|
22
28
|
const normalized = sqlType.toLowerCase().replace(/\([^)]*\)/g, '').trim();
|
|
23
29
|
if (['integer', 'int', 'int4', 'smallint', 'int2', 'bigint', 'int8',
|
|
24
30
|
'numeric', 'decimal', 'real', 'float4', 'float8', 'double precision',
|
|
@@ -40,6 +46,8 @@ function inferTsTypeFromSqlType(sqlType) {
|
|
|
40
46
|
return 'string';
|
|
41
47
|
}
|
|
42
48
|
function generateColumnBuilderFromType(sqlType) {
|
|
49
|
+
if (!sqlType)
|
|
50
|
+
return 'text()';
|
|
43
51
|
const normalized = sqlType.toLowerCase().trim();
|
|
44
52
|
const paramMatch = normalized.match(/^([a-z_][a-z0-9_\s]*?)(?:\s*\(([^)]+)\))?$/i);
|
|
45
53
|
if (!paramMatch)
|
|
@@ -257,6 +265,10 @@ function splitCheckByAnd(expr) {
|
|
|
257
265
|
return parts;
|
|
258
266
|
}
|
|
259
267
|
function getColumnType(col, enumNames, domainNames, compositeNames) {
|
|
268
|
+
if (!col.dataType) {
|
|
269
|
+
console.warn(`Warning: Column ${col.name} has no dataType, defaulting to text`);
|
|
270
|
+
return 'text()';
|
|
271
|
+
}
|
|
260
272
|
let pgType = col.dataType.toLowerCase();
|
|
261
273
|
const isArray = pgType.startsWith('_') || pgType.endsWith('[]');
|
|
262
274
|
if (pgType.startsWith('_')) {
|
|
@@ -414,12 +426,11 @@ function parseGeneratedExpression(expr, camelCase) {
|
|
|
414
426
|
const colName = camelCase ? toCamelCase(expr) : expr;
|
|
415
427
|
return `table.${colName}`;
|
|
416
428
|
}
|
|
417
|
-
const
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
}
|
|
422
|
-
return `table.id /* TODO: Fix expression: ${expr.replace(/\*\//g, '* /')} */`;
|
|
429
|
+
const escapedExpr = expr
|
|
430
|
+
.replace(/\\/g, '\\\\')
|
|
431
|
+
.replace(/`/g, '\\`')
|
|
432
|
+
.replace(/\$\{/g, '\\${');
|
|
433
|
+
return `sql\`${escapedExpr}\``;
|
|
423
434
|
}
|
|
424
435
|
function parseArithmeticExpression(expr, camelCase) {
|
|
425
436
|
const tokens = [];
|
|
@@ -664,6 +675,13 @@ function formatDefaultValue(val, col, domainMap) {
|
|
|
664
675
|
return 'true';
|
|
665
676
|
if (cleaned.toLowerCase() === 'false')
|
|
666
677
|
return 'false';
|
|
678
|
+
if (!col.dataType) {
|
|
679
|
+
if (cleaned.startsWith("'") && cleaned.endsWith("'")) {
|
|
680
|
+
const str = cleaned.slice(1, -1).replace(/'/g, "\\'");
|
|
681
|
+
return `'${str}'`;
|
|
682
|
+
}
|
|
683
|
+
return null;
|
|
684
|
+
}
|
|
667
685
|
let baseDataType = col.dataType.toLowerCase().replace(/\([^)]*\)$/, '').trim();
|
|
668
686
|
if (domainMap && domainMap.has(baseDataType)) {
|
|
669
687
|
baseDataType = domainMap.get(baseDataType).toLowerCase().replace(/\([^)]*\)$/, '').trim();
|
|
@@ -675,7 +693,7 @@ function formatDefaultValue(val, col, domainMap) {
|
|
|
675
693
|
return `'${cleaned}'`;
|
|
676
694
|
}
|
|
677
695
|
else if (isBigInt) {
|
|
678
|
-
return
|
|
696
|
+
return `${cleaned}n`;
|
|
679
697
|
}
|
|
680
698
|
return cleaned;
|
|
681
699
|
}
|
|
@@ -752,7 +770,7 @@ function formatDefaultValue(val, col, domainMap) {
|
|
|
752
770
|
}
|
|
753
771
|
return null;
|
|
754
772
|
}
|
|
755
|
-
function generateColumnDef(col, seenColumns, camelCase = true,
|
|
773
|
+
function generateColumnDef(col, seenColumns, camelCase = true, checkInfo, enumNames, domainNames, compositeNames, domainMap) {
|
|
756
774
|
if (seenColumns.has(col.name)) {
|
|
757
775
|
return null;
|
|
758
776
|
}
|
|
@@ -774,9 +792,9 @@ function generateColumnDef(col, seenColumns, camelCase = true, checkValues, enum
|
|
|
774
792
|
typeCall = colType;
|
|
775
793
|
}
|
|
776
794
|
const parts = [typeCall];
|
|
777
|
-
if (
|
|
778
|
-
const quotedValues =
|
|
779
|
-
parts.push(`.check(${quotedValues})`);
|
|
795
|
+
if (checkInfo && checkInfo.values.length > 0) {
|
|
796
|
+
const quotedValues = checkInfo.values.map(v => `'${v}'`).join(', ');
|
|
797
|
+
parts.push(`.check('${checkInfo.name}', [${quotedValues}])`);
|
|
780
798
|
}
|
|
781
799
|
if (col.isPrimaryKey) {
|
|
782
800
|
parts.push('.primaryKey()');
|
|
@@ -1554,6 +1572,36 @@ function generateCheckConstraints(constraints, camelCase) {
|
|
|
1554
1572
|
function generateDefineTable(table, camelCase = true, childPartitions, enumNames, domainNames, compositeNames, domainMap) {
|
|
1555
1573
|
const tableName = toCamelCase(table.name);
|
|
1556
1574
|
const seenColumns = new Set();
|
|
1575
|
+
const pkColumns = new Set();
|
|
1576
|
+
const compositePKs = [];
|
|
1577
|
+
const pkConstraints = (table.constraints || []).filter(c => c.type === 'PRIMARY KEY');
|
|
1578
|
+
for (const pk of pkConstraints) {
|
|
1579
|
+
const match = pk.definition.match(/PRIMARY\s+KEY\s*\(([^)]+)\)/i);
|
|
1580
|
+
if (match) {
|
|
1581
|
+
const cols = match[1].split(',').map(c => c.trim().replace(/"/g, ''));
|
|
1582
|
+
if (cols.length === 1) {
|
|
1583
|
+
pkColumns.add(cols[0]);
|
|
1584
|
+
}
|
|
1585
|
+
else if (cols.length > 1) {
|
|
1586
|
+
compositePKs.push({ name: pk.name, columns: cols });
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
const uniqueColumns = new Set();
|
|
1591
|
+
const compositeUniques = [];
|
|
1592
|
+
const uniqueConstraints = (table.constraints || []).filter(c => c.type === 'UNIQUE');
|
|
1593
|
+
for (const uq of uniqueConstraints) {
|
|
1594
|
+
const match = uq.definition.match(/UNIQUE\s*\(([^)]+)\)/i);
|
|
1595
|
+
if (match) {
|
|
1596
|
+
const cols = match[1].split(',').map(c => c.trim().replace(/"/g, ''));
|
|
1597
|
+
if (cols.length === 1) {
|
|
1598
|
+
uniqueColumns.add(cols[0]);
|
|
1599
|
+
}
|
|
1600
|
+
else if (cols.length > 1) {
|
|
1601
|
+
compositeUniques.push({ name: uq.name, columns: cols });
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1557
1605
|
const checkConstraints = (table.constraints || []).filter(c => c.type === 'CHECK');
|
|
1558
1606
|
const columnChecks = new Map();
|
|
1559
1607
|
for (const check of checkConstraints) {
|
|
@@ -1563,14 +1611,19 @@ function generateDefineTable(table, camelCase = true, childPartitions, enumNames
|
|
|
1563
1611
|
const valuesStr = enumMatch[2];
|
|
1564
1612
|
const values = valuesStr.match(/'([^']+)'/g)?.map(v => v.replace(/'/g, '')) || [];
|
|
1565
1613
|
if (values.length > 0) {
|
|
1566
|
-
columnChecks.set(colName, values);
|
|
1614
|
+
columnChecks.set(colName, { name: check.name, values });
|
|
1567
1615
|
}
|
|
1568
1616
|
}
|
|
1569
1617
|
}
|
|
1570
1618
|
const columns = table.columns
|
|
1571
1619
|
.map(col => {
|
|
1572
|
-
const
|
|
1573
|
-
|
|
1620
|
+
const colWithConstraints = {
|
|
1621
|
+
...col,
|
|
1622
|
+
isPrimaryKey: pkColumns.has(col.name) ? true : col.isPrimaryKey,
|
|
1623
|
+
isUnique: uniqueColumns.has(col.name) ? true : col.isUnique,
|
|
1624
|
+
};
|
|
1625
|
+
const checkInfo = columnChecks.get(col.name);
|
|
1626
|
+
return generateColumnDef(colWithConstraints, seenColumns, camelCase, checkInfo, enumNames, domainNames, compositeNames, domainMap);
|
|
1574
1627
|
})
|
|
1575
1628
|
.filter(Boolean);
|
|
1576
1629
|
const columnNames = Array.from(seenColumns);
|
|
@@ -1578,7 +1631,10 @@ function generateDefineTable(table, camelCase = true, childPartitions, enumNames
|
|
|
1578
1631
|
const checkConstraintsFn = table.constraints ? generateCheckConstraints(table.constraints, camelCase) : null;
|
|
1579
1632
|
const hasPartition = table.isPartitioned && table.partitionType && table.partitionKey?.length;
|
|
1580
1633
|
const hasChildPartitions = childPartitions && childPartitions.length > 0;
|
|
1581
|
-
|
|
1634
|
+
const hasCompositePK = compositePKs.length > 0;
|
|
1635
|
+
const hasCompositeUnique = compositeUniques.length > 0;
|
|
1636
|
+
const hasTableConstraints = hasCompositePK || hasCompositeUnique;
|
|
1637
|
+
if (hasPartition || indexesFn || hasChildPartitions || checkConstraintsFn || hasTableConstraints) {
|
|
1582
1638
|
const parts = [];
|
|
1583
1639
|
if (hasPartition) {
|
|
1584
1640
|
let rawPartitionKey = table.partitionKey || [];
|
|
@@ -1631,6 +1687,18 @@ function generateDefineTable(table, camelCase = true, childPartitions, enumNames
|
|
|
1631
1687
|
});
|
|
1632
1688
|
parts.push(` partitions: (partition) => [\n${partitionLines.join(',\n')},\n ]`);
|
|
1633
1689
|
}
|
|
1690
|
+
if (hasTableConstraints) {
|
|
1691
|
+
const constraintLines = [];
|
|
1692
|
+
for (const pk of compositePKs) {
|
|
1693
|
+
const colRefs = pk.columns.map(c => `table.${camelCase ? toCamelCase(c) : c}`).join(', ');
|
|
1694
|
+
constraintLines.push(` constraint.primaryKey('${pk.name}', ${colRefs})`);
|
|
1695
|
+
}
|
|
1696
|
+
for (const uq of compositeUniques) {
|
|
1697
|
+
const colRefs = uq.columns.map(c => `table.${camelCase ? toCamelCase(c) : c}`).join(', ');
|
|
1698
|
+
constraintLines.push(` constraint.unique('${uq.name}', ${colRefs})`);
|
|
1699
|
+
}
|
|
1700
|
+
parts.push(` constraints: (table, constraint) => [\n${constraintLines.join(',\n')},\n ]`);
|
|
1701
|
+
}
|
|
1634
1702
|
if (checkConstraintsFn) {
|
|
1635
1703
|
parts.push(` checkConstraints: ${checkConstraintsFn.trim()}`);
|
|
1636
1704
|
}
|
|
@@ -1665,6 +1733,10 @@ export function generateTypeScript(schema, options = {}) {
|
|
|
1665
1733
|
const imports = new Set(['defineTable']);
|
|
1666
1734
|
for (const table of tables) {
|
|
1667
1735
|
for (const col of table.columns) {
|
|
1736
|
+
if (!col.dataType) {
|
|
1737
|
+
console.warn(`Warning: Column ${table.name}.${col.name} has no dataType, skipping`);
|
|
1738
|
+
continue;
|
|
1739
|
+
}
|
|
1668
1740
|
const rawType = col.dataType.toLowerCase().replace(/^_/, '');
|
|
1669
1741
|
const pgType = rawType.replace(/\([^)]*\)$/, '').trim();
|
|
1670
1742
|
if (['int4', 'integer'].includes(pgType))
|
|
@@ -1750,6 +1822,15 @@ export function generateTypeScript(schema, options = {}) {
|
|
|
1750
1822
|
if (d.includes("'[]'") || d.includes('array[]'))
|
|
1751
1823
|
imports.add('emptyArray');
|
|
1752
1824
|
}
|
|
1825
|
+
if (col.isGenerated && col.generationExpression) {
|
|
1826
|
+
const expr = col.generationExpression.toLowerCase();
|
|
1827
|
+
const complexFunctions = ['setweight', 'to_tsvector', 'plainto_tsquery', 'ts_rank',
|
|
1828
|
+
'regexp_replace', 'array_agg', 'string_agg', 'jsonb_build_object',
|
|
1829
|
+
'jsonb_agg', 'row_to_json', 'json_build_object'];
|
|
1830
|
+
if (complexFunctions.some(f => expr.includes(f))) {
|
|
1831
|
+
imports.add('sql');
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1753
1834
|
}
|
|
1754
1835
|
}
|
|
1755
1836
|
const userFunctions = schema.functions?.filter((f) => !f.name.startsWith('pg_') &&
|
|
@@ -1759,6 +1840,9 @@ export function generateTypeScript(schema, options = {}) {
|
|
|
1759
1840
|
}
|
|
1760
1841
|
if (includeTriggers && schema.triggers && schema.triggers.length > 0) {
|
|
1761
1842
|
imports.add('pgTrigger');
|
|
1843
|
+
if (!includeFunctions) {
|
|
1844
|
+
imports.add('pgFunction');
|
|
1845
|
+
}
|
|
1762
1846
|
}
|
|
1763
1847
|
if (schema.sequences && schema.sequences.length > 0) {
|
|
1764
1848
|
imports.add('pgSequence');
|
|
@@ -2014,6 +2098,21 @@ export function generateTypeScript(schema, options = {}) {
|
|
|
2014
2098
|
const validTriggers = schema.triggers.filter((t) => tableNames.has(t.tableName.toLowerCase()) &&
|
|
2015
2099
|
!partitionTableNames.has(t.tableName.toLowerCase()));
|
|
2016
2100
|
if (validTriggers.length > 0) {
|
|
2101
|
+
const triggerFuncNames = new Set();
|
|
2102
|
+
for (const trigger of validTriggers) {
|
|
2103
|
+
triggerFuncNames.add(trigger.functionName);
|
|
2104
|
+
}
|
|
2105
|
+
if (!includeFunctions && triggerFuncNames.size > 0) {
|
|
2106
|
+
parts.push('// ============================================');
|
|
2107
|
+
parts.push('// Trigger Function References');
|
|
2108
|
+
parts.push('// ============================================');
|
|
2109
|
+
parts.push('');
|
|
2110
|
+
for (const funcName of triggerFuncNames) {
|
|
2111
|
+
const funcCamel = toCamelCase(funcName);
|
|
2112
|
+
parts.push(`export const ${funcCamel} = pgFunction('${funcName}', { returns: 'trigger', language: 'plpgsql' });`);
|
|
2113
|
+
}
|
|
2114
|
+
parts.push('');
|
|
2115
|
+
}
|
|
2017
2116
|
parts.push('// ============================================');
|
|
2018
2117
|
parts.push('// Database Triggers');
|
|
2019
2118
|
parts.push('// ============================================');
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
|
-
import { toPoolConfig } from "../types/config-types.js";
|
|
2
|
+
import { toPoolConfig, isAwsDsqlConfig } from "../types/config-types.js";
|
|
3
|
+
import { getAwsDsqlToken } from "../utils/aws-dsql.js";
|
|
3
4
|
import { SelectBuilder } from "../select/select-builder.js";
|
|
4
5
|
import { AggregateQueryBuilder } from "../select/aggregate-builder.js";
|
|
5
6
|
import { InsertBuilder } from "../insert/insert-builder.js";
|
|
@@ -411,8 +412,18 @@ export class Relq {
|
|
|
411
412
|
}
|
|
412
413
|
this.initPromise = (async () => {
|
|
413
414
|
const { Pool: PgPoolClass, Client: PgClientClass } = await loadPg();
|
|
415
|
+
let resolvedPassword = this.config.password;
|
|
416
|
+
if (isAwsDsqlConfig(this.config)) {
|
|
417
|
+
debugLog(this.config, 'Resolving AWS DSQL token...');
|
|
418
|
+
resolvedPassword = await getAwsDsqlToken(this.config.aws);
|
|
419
|
+
debugLog(this.config, 'AWS DSQL token resolved successfully');
|
|
420
|
+
}
|
|
414
421
|
if (this.usePooling) {
|
|
415
|
-
|
|
422
|
+
const poolConfig = toPoolConfig(this.config);
|
|
423
|
+
if (resolvedPassword) {
|
|
424
|
+
poolConfig.password = resolvedPassword;
|
|
425
|
+
}
|
|
426
|
+
this.pool = new PgPoolClass(poolConfig);
|
|
416
427
|
this.setupPoolErrorHandling();
|
|
417
428
|
this.pool.on('connect', (client) => {
|
|
418
429
|
this.emitter.emit('connect', client);
|
|
@@ -422,14 +433,19 @@ export class Relq {
|
|
|
422
433
|
this.pool.on('remove', (client) => this.emitter.emit('remove', client));
|
|
423
434
|
}
|
|
424
435
|
else {
|
|
436
|
+
const isAws = isAwsDsqlConfig(this.config);
|
|
437
|
+
const host = isAws ? this.config.aws.hostname : (this.config.host || 'localhost');
|
|
438
|
+
const port = this.config.aws?.port ?? this.config.port ?? 5432;
|
|
439
|
+
const user = this.config.aws?.user ?? this.config.user ?? (isAws ? 'admin' : undefined);
|
|
440
|
+
const ssl = isAws ? (this.config.aws.ssl ?? true) : this.config.ssl;
|
|
425
441
|
this.client = new PgClientClass({
|
|
426
|
-
host
|
|
427
|
-
port
|
|
442
|
+
host,
|
|
443
|
+
port,
|
|
428
444
|
database: this.config.database,
|
|
429
|
-
user
|
|
430
|
-
password:
|
|
445
|
+
user,
|
|
446
|
+
password: resolvedPassword,
|
|
431
447
|
connectionString: this.config.connectionString,
|
|
432
|
-
ssl
|
|
448
|
+
ssl
|
|
433
449
|
});
|
|
434
450
|
this.client.on('end', () => this.emitter.emit('end'));
|
|
435
451
|
this.client.on('error', (err) => this.emitter.emit('error', err));
|
|
@@ -29,6 +29,111 @@ function chainableExpr(sql) {
|
|
|
29
29
|
};
|
|
30
30
|
return expr;
|
|
31
31
|
}
|
|
32
|
+
export function createFluentGenExpr(sql) {
|
|
33
|
+
const self = {
|
|
34
|
+
$sql: sql,
|
|
35
|
+
$expr: true,
|
|
36
|
+
coalesce(fallback) {
|
|
37
|
+
const fallbackSql = typeof fallback === 'object' && '$sql' in fallback
|
|
38
|
+
? fallback.$sql
|
|
39
|
+
: formatGenValue(fallback);
|
|
40
|
+
return createFluentGenExpr(`COALESCE(${this.$sql}, ${fallbackSql})`);
|
|
41
|
+
},
|
|
42
|
+
nullif(value) {
|
|
43
|
+
return createFluentGenExpr(`NULLIF(${this.$sql}, ${formatGenValue(value)})`);
|
|
44
|
+
},
|
|
45
|
+
asText() {
|
|
46
|
+
return createFluentGenExpr(`(${this.$sql})::TEXT`);
|
|
47
|
+
},
|
|
48
|
+
asInteger() {
|
|
49
|
+
return createFluentGenExpr(`(${this.$sql})::INTEGER`);
|
|
50
|
+
},
|
|
51
|
+
asVarchar() {
|
|
52
|
+
return createFluentGenExpr(`(${this.$sql})::VARCHAR`);
|
|
53
|
+
},
|
|
54
|
+
cast(typeName) {
|
|
55
|
+
return createFluentGenExpr(`(${this.$sql})::${typeName}`);
|
|
56
|
+
},
|
|
57
|
+
lower() {
|
|
58
|
+
return createFluentGenExpr(`LOWER(${this.$sql})`);
|
|
59
|
+
},
|
|
60
|
+
upper() {
|
|
61
|
+
return createFluentGenExpr(`UPPER(${this.$sql})`);
|
|
62
|
+
},
|
|
63
|
+
trim() {
|
|
64
|
+
return createFluentGenExpr(`TRIM(${this.$sql})`);
|
|
65
|
+
},
|
|
66
|
+
concat(...parts) {
|
|
67
|
+
const partsSql = parts.map(p => typeof p === 'object' && '$sql' in p ? p.$sql : formatGenValue(p));
|
|
68
|
+
return createFluentGenExpr(`(${[this.$sql, ...partsSql].join(' || ')})`);
|
|
69
|
+
},
|
|
70
|
+
substring(start, length) {
|
|
71
|
+
return length !== undefined
|
|
72
|
+
? createFluentGenExpr(`SUBSTRING(${this.$sql} FROM ${start} FOR ${length})`)
|
|
73
|
+
: createFluentGenExpr(`SUBSTRING(${this.$sql} FROM ${start})`);
|
|
74
|
+
},
|
|
75
|
+
replace(from, to) {
|
|
76
|
+
return createFluentGenExpr(`REPLACE(${this.$sql}, '${from.replace(/'/g, "''")}', '${to.replace(/'/g, "''")}')`);
|
|
77
|
+
},
|
|
78
|
+
length() {
|
|
79
|
+
return createFluentGenExpr(`LENGTH(${this.$sql})`);
|
|
80
|
+
},
|
|
81
|
+
lpad(len, fill = ' ') {
|
|
82
|
+
return createFluentGenExpr(`LPAD(${this.$sql}, ${len}, '${fill}')`);
|
|
83
|
+
},
|
|
84
|
+
rpad(len, fill = ' ') {
|
|
85
|
+
return createFluentGenExpr(`RPAD(${this.$sql}, ${len}, '${fill}')`);
|
|
86
|
+
},
|
|
87
|
+
toTsvector(config) {
|
|
88
|
+
return createFluentGenExpr(`TO_TSVECTOR('${config}', ${this.$sql})`);
|
|
89
|
+
},
|
|
90
|
+
setWeight(weight) {
|
|
91
|
+
return createFluentGenExpr(`SETWEIGHT(${this.$sql}, '${weight}')`);
|
|
92
|
+
},
|
|
93
|
+
tsvConcat(other) {
|
|
94
|
+
return createFluentGenExpr(`(${this.$sql} || ${other.$sql})`);
|
|
95
|
+
},
|
|
96
|
+
jsonExtract(key) {
|
|
97
|
+
return createFluentGenExpr(`${this.$sql}->'${key}'`);
|
|
98
|
+
},
|
|
99
|
+
jsonExtractText(key) {
|
|
100
|
+
return createFluentGenExpr(`${this.$sql}->>'${key}'`);
|
|
101
|
+
},
|
|
102
|
+
add(value) {
|
|
103
|
+
return createFluentGenExpr(`(${this.$sql} + ${formatGenValue(value)})`);
|
|
104
|
+
},
|
|
105
|
+
subtract(value) {
|
|
106
|
+
return createFluentGenExpr(`(${this.$sql} - ${formatGenValue(value)})`);
|
|
107
|
+
},
|
|
108
|
+
multiply(value) {
|
|
109
|
+
return createFluentGenExpr(`(${this.$sql} * ${formatGenValue(value)})`);
|
|
110
|
+
},
|
|
111
|
+
divide(value) {
|
|
112
|
+
return createFluentGenExpr(`(${this.$sql} / ${formatGenValue(value)})`);
|
|
113
|
+
},
|
|
114
|
+
abs() {
|
|
115
|
+
return createFluentGenExpr(`ABS(${this.$sql})`);
|
|
116
|
+
},
|
|
117
|
+
round(precision) {
|
|
118
|
+
return precision !== undefined
|
|
119
|
+
? createFluentGenExpr(`ROUND(${this.$sql}, ${precision})`)
|
|
120
|
+
: createFluentGenExpr(`ROUND(${this.$sql})`);
|
|
121
|
+
},
|
|
122
|
+
floor() {
|
|
123
|
+
return createFluentGenExpr(`FLOOR(${this.$sql})`);
|
|
124
|
+
},
|
|
125
|
+
ceil() {
|
|
126
|
+
return createFluentGenExpr(`CEIL(${this.$sql})`);
|
|
127
|
+
},
|
|
128
|
+
extract(field) {
|
|
129
|
+
return createFluentGenExpr(`EXTRACT(${field.toUpperCase()} FROM ${this.$sql})`);
|
|
130
|
+
},
|
|
131
|
+
arrayLength(dimension = 1) {
|
|
132
|
+
return createFluentGenExpr(`ARRAY_LENGTH(${this.$sql}, ${dimension})`);
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
return self;
|
|
136
|
+
}
|
|
32
137
|
function createCaseBuilder() {
|
|
33
138
|
const whens = [];
|
|
34
139
|
let elseResult = null;
|
|
@@ -119,6 +224,23 @@ const generatedFnMethods = {
|
|
|
119
224
|
arrayPosition: (arr, elem) => chainableExpr(`ARRAY_POSITION(${arr.$sql}, ${formatGenValue(elem)})`),
|
|
120
225
|
md5: (col) => chainableExpr(`MD5(${col.$sql})`),
|
|
121
226
|
sha256: (col) => chainableExpr(`ENCODE(SHA256(${col.$sql}::BYTEA), 'hex')`),
|
|
227
|
+
col: (name, alias) => chainableExpr(alias ? `"${alias}"."${name}"` : `"${name}"`),
|
|
228
|
+
cast: (value, typeName) => chainableExpr(`(${formatGenValue(value)})::${typeName}`),
|
|
229
|
+
func: (name, ...args) => chainableExpr(`${name.toUpperCase()}(${args.map(formatGenValue).join(', ')})`),
|
|
230
|
+
sql: (expression) => chainableExpr(expression),
|
|
231
|
+
op: (left, operator, right) => chainableExpr(`(${formatGenValue(left)} ${operator} ${formatGenValue(right)})`),
|
|
232
|
+
setweight: (tsvector, weight) => chainableExpr(`SETWEIGHT(${tsvector.$sql}, '${weight}')`),
|
|
233
|
+
setWeight: (tsvector, weight) => chainableExpr(`SETWEIGHT(${tsvector.$sql}, '${weight}')`),
|
|
234
|
+
asVarchar: (col) => chainableExpr(`(${col.$sql})::VARCHAR`),
|
|
235
|
+
textConcat: (left, right) => chainableExpr(`(${formatGenValue(left)} || ${formatGenValue(right)})`),
|
|
236
|
+
tsMatch: (left, right) => chainableExpr(`(${left.$sql} @@ ${right.$sql})`),
|
|
237
|
+
compare: (left, op, right) => chainableExpr(`(${formatGenValue(left)} ${op} ${formatGenValue(right)})`),
|
|
238
|
+
regex: (value, op, pattern) => chainableExpr(`(${value.$sql} ${op} ${formatGenValue(pattern)})`),
|
|
239
|
+
and: (...args) => chainableExpr(`(${args.map(a => a.$sql).join(' AND ')})`),
|
|
240
|
+
or: (...args) => chainableExpr(`(${args.map(a => a.$sql).join(' OR ')})`),
|
|
241
|
+
not: (arg) => chainableExpr(`(NOT ${arg.$sql})`),
|
|
242
|
+
isNull: (col) => chainableExpr(`(${col.$sql} IS NULL)`),
|
|
243
|
+
isNotNull: (col) => chainableExpr(`(${col.$sql} IS NOT NULL)`),
|
|
122
244
|
};
|
|
123
245
|
const generatedFn = new Proxy(function (col) {
|
|
124
246
|
return chainableExpr(col.$sql);
|
|
@@ -159,21 +281,19 @@ function createColumn(type) {
|
|
|
159
281
|
config.$references = { table, column, ...options };
|
|
160
282
|
return Object.assign(this, { $references: config.$references });
|
|
161
283
|
},
|
|
162
|
-
check(
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
return Object.assign(this, { $check: values[0] });
|
|
166
|
-
}
|
|
167
|
-
const expression = `IN ('${values.join("', '")}')`;
|
|
284
|
+
check(name, values) {
|
|
285
|
+
const expression = `${name} IN ('${values.join("', '")}')`;
|
|
286
|
+
config.$checkName = name;
|
|
168
287
|
config.$checkValues = values;
|
|
169
288
|
config.$check = expression;
|
|
170
|
-
return Object.assign(this, { $check: expression, $checkValues: values });
|
|
289
|
+
return Object.assign(this, { $check: expression, $checkName: name, $checkValues: values });
|
|
171
290
|
},
|
|
172
|
-
checkNot(
|
|
173
|
-
const expression =
|
|
291
|
+
checkNot(name, values) {
|
|
292
|
+
const expression = `${name} NOT IN ('${values.join("', '")}')`;
|
|
293
|
+
config.$checkNotName = name;
|
|
174
294
|
config.$checkNotValues = values;
|
|
175
295
|
config.$checkNot = expression;
|
|
176
|
-
return Object.assign(this, { $checkNot: expression, $checkNotValues: values });
|
|
296
|
+
return Object.assign(this, { $checkNot: expression, $checkNotName: name, $checkNotValues: values });
|
|
177
297
|
},
|
|
178
298
|
generatedAs(expression, stored = true) {
|
|
179
299
|
config.$generated = { expression, stored };
|
|
@@ -234,6 +354,14 @@ function createColumn(type) {
|
|
|
234
354
|
config.$dimensions = d;
|
|
235
355
|
config.$type = `VECTOR(${d})`;
|
|
236
356
|
return Object.assign(this, { $dimensions: d, $type: config.$type });
|
|
357
|
+
},
|
|
358
|
+
comment(text) {
|
|
359
|
+
config.$comment = text;
|
|
360
|
+
return Object.assign(this, { $comment: text });
|
|
361
|
+
},
|
|
362
|
+
$id(trackingId) {
|
|
363
|
+
config.$trackingId = trackingId;
|
|
364
|
+
return Object.assign(this, { $trackingId: trackingId });
|
|
237
365
|
}
|
|
238
366
|
};
|
|
239
367
|
return builder;
|
|
@@ -700,10 +828,16 @@ export function generateCompositeTypeSQL(composite) {
|
|
|
700
828
|
}
|
|
701
829
|
export const customType = (typeName, columnName) => createColumnWithName(typeName, columnName);
|
|
702
830
|
export const enumType = (name, values, columnName) => {
|
|
703
|
-
const
|
|
704
|
-
|
|
831
|
+
const enumName = typeof name === 'string' ? name : name.name;
|
|
832
|
+
const enumValues = typeof name === 'string' ? (values || []) : name.values;
|
|
833
|
+
const col = createColumnWithName(enumName, columnName);
|
|
834
|
+
col.$enumValues = enumValues;
|
|
705
835
|
return col;
|
|
706
836
|
};
|
|
837
|
+
export const domainType = (domainDef, columnName) => {
|
|
838
|
+
const domainName = typeof domainDef === 'string' ? domainDef : domainDef.$domainName;
|
|
839
|
+
return createColumnWithName(domainName, columnName);
|
|
840
|
+
};
|
|
707
841
|
export const compositeType = (typeName, columnName) => createColumnWithName(typeName, columnName);
|
|
708
842
|
export const SQL_BRAND = Symbol.for('relq.sql.brand');
|
|
709
843
|
function sqlExpr(sql) {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
function createDefault(sql) {
|
|
2
|
+
return { $sql: sql, $isDefault: true };
|
|
3
|
+
}
|
|
4
|
+
export const DEFAULT = {
|
|
5
|
+
genRandomUuid: () => createDefault('gen_random_uuid()'),
|
|
6
|
+
uuidGenerateV4: () => createDefault('uuid_generate_v4()'),
|
|
7
|
+
uuidGenerateV1: () => createDefault('uuid_generate_v1()'),
|
|
8
|
+
uuidGenerateV1mc: () => createDefault('uuid_generate_v1mc()'),
|
|
9
|
+
uuidNil: () => createDefault('uuid_nil()'),
|
|
10
|
+
now: () => createDefault('NOW()'),
|
|
11
|
+
currentTimestamp: () => createDefault('CURRENT_TIMESTAMP'),
|
|
12
|
+
currentDate: () => createDefault('CURRENT_DATE'),
|
|
13
|
+
currentTime: () => createDefault('CURRENT_TIME'),
|
|
14
|
+
localTimestamp: () => createDefault('LOCALTIMESTAMP'),
|
|
15
|
+
localTime: () => createDefault('LOCALTIME'),
|
|
16
|
+
transactionTimestamp: () => createDefault('transaction_timestamp()'),
|
|
17
|
+
statementTimestamp: () => createDefault('statement_timestamp()'),
|
|
18
|
+
clockTimestamp: () => createDefault('clock_timestamp()'),
|
|
19
|
+
timeofday: () => createDefault('timeofday()'),
|
|
20
|
+
interval: (value) => createDefault(`INTERVAL '${value}'`),
|
|
21
|
+
currentUser: () => createDefault('CURRENT_USER'),
|
|
22
|
+
sessionUser: () => createDefault('SESSION_USER'),
|
|
23
|
+
user: () => createDefault('USER'),
|
|
24
|
+
currentSchema: () => createDefault('current_schema()'),
|
|
25
|
+
currentDatabase: () => createDefault('current_database()'),
|
|
26
|
+
currentCatalog: () => createDefault('current_catalog'),
|
|
27
|
+
inetClientAddr: () => createDefault('inet_client_addr()'),
|
|
28
|
+
inetClientPort: () => createDefault('inet_client_port()'),
|
|
29
|
+
inetServerAddr: () => createDefault('inet_server_addr()'),
|
|
30
|
+
inetServerPort: () => createDefault('inet_server_port()'),
|
|
31
|
+
pgBackendPid: () => createDefault('pg_backend_pid()'),
|
|
32
|
+
nextval: (sequenceName) => createDefault(`nextval('${sequenceName}'::regclass)`),
|
|
33
|
+
currval: (sequenceName) => createDefault(`currval('${sequenceName}'::regclass)`),
|
|
34
|
+
lastval: () => createDefault('lastval()'),
|
|
35
|
+
random: () => createDefault('random()'),
|
|
36
|
+
pi: () => createDefault('pi()'),
|
|
37
|
+
emptyString: () => createDefault("''"),
|
|
38
|
+
emptyObject: () => createDefault("'{}'::jsonb"),
|
|
39
|
+
emptyJson: () => createDefault("'{}'::json"),
|
|
40
|
+
emptyJsonb: () => createDefault("'{}'::jsonb"),
|
|
41
|
+
emptyArray: () => createDefault("'{}'"),
|
|
42
|
+
emptyArrayOf: (type) => createDefault(`ARRAY[]::${type}[]`),
|
|
43
|
+
true: () => createDefault('TRUE'),
|
|
44
|
+
false: () => createDefault('FALSE'),
|
|
45
|
+
null: () => createDefault('NULL'),
|
|
46
|
+
zero: () => createDefault('0'),
|
|
47
|
+
one: () => createDefault('1'),
|
|
48
|
+
negativeOne: () => createDefault('-1'),
|
|
49
|
+
string: (value) => createDefault(`'${value.replace(/'/g, "''")}'`),
|
|
50
|
+
number: (value) => createDefault(String(value)),
|
|
51
|
+
integer: (value) => createDefault(String(Math.floor(value))),
|
|
52
|
+
decimal: (value, precision) => createDefault(precision !== undefined ? value.toFixed(precision) : String(value)),
|
|
53
|
+
cast: (value, type) => createDefault(`${typeof value === 'string' ? `'${value}'` : value}::${type}`),
|
|
54
|
+
emptyTsvector: () => createDefault("''::tsvector"),
|
|
55
|
+
point: (x, y) => createDefault(`point(${x}, ${y})`),
|
|
56
|
+
inet: (address) => createDefault(`'${address}'::inet`),
|
|
57
|
+
cidr: (network) => createDefault(`'${network}'::cidr`),
|
|
58
|
+
macaddr: (address) => createDefault(`'${address}'::macaddr`),
|
|
59
|
+
emptyInt4range: () => createDefault("'empty'::int4range"),
|
|
60
|
+
emptyInt8range: () => createDefault("'empty'::int8range"),
|
|
61
|
+
emptyNumrange: () => createDefault("'empty'::numrange"),
|
|
62
|
+
emptyTsrange: () => createDefault("'empty'::tsrange"),
|
|
63
|
+
emptyTstzrange: () => createDefault("'empty'::tstzrange"),
|
|
64
|
+
emptyDaterange: () => createDefault("'empty'::daterange"),
|
|
65
|
+
emptyHstore: () => createDefault("''::hstore"),
|
|
66
|
+
emptyBytea: () => createDefault("'\\x'::bytea"),
|
|
67
|
+
money: (value) => createDefault(`'${value}'::money`),
|
|
68
|
+
zeroMoney: () => createDefault("'0'::money"),
|
|
69
|
+
};
|
|
@@ -3,8 +3,11 @@ export { defineTable } from "./table-definition.js";
|
|
|
3
3
|
export { sqlFunctions, pgExtensions, getSql, createWhereBuilder, createGeneratedExprBuilder, createTableColumnRefs, expressionBuilder, createExpressionBuilder, } from "./sql-expressions.js";
|
|
4
4
|
export { parseCreateTable, generateSchemaCode, introspectSQL, introspectMultiple } from "./introspection.js";
|
|
5
5
|
export { one, many, manyToMany } from "./relations.js";
|
|
6
|
+
export { pgRelations, defineRelations, generateReferencesSQL, actionCodeToString, stringToActionCode, matchCodeToString, } from "./pg-relations.js";
|
|
6
7
|
export { pgEnum, generateEnumSQL, dropEnumSQL, addEnumValueSQL } from "./pg-enum.js";
|
|
7
8
|
export { pgFunction, generateFunctionSQL, dropFunctionSQL, isFunctionConfig, } from "./pg-function.js";
|
|
8
9
|
export { pgTrigger, generateTriggerSQL, dropTriggerSQL, isTriggerConfig, } from "./pg-trigger.js";
|
|
9
10
|
export { pgSequence, generateSequenceSQL, dropSequenceSQL, isSequenceConfig, } from "./pg-sequence.js";
|
|
11
|
+
export { pgView, pgMaterializedView, viewToSQL, materializedViewToSQL, } from "./pg-view.js";
|
|
10
12
|
export { partitionStrategyFactory, generatePartitionBySQL, generateChildPartitionSQL, } from "./partitions.js";
|
|
13
|
+
export { DEFAULT } from "./defaults.js";
|
|
@@ -472,7 +472,7 @@ export function generateSchemaCode(table, options) {
|
|
|
472
472
|
modifiers.push('.unique()');
|
|
473
473
|
}
|
|
474
474
|
if (col.default !== undefined) {
|
|
475
|
-
const defaultVal = formatDefaultValue(col.default);
|
|
475
|
+
const defaultVal = formatDefaultValue(col.default, col.type);
|
|
476
476
|
modifiers.push(`.default(${defaultVal})`);
|
|
477
477
|
}
|
|
478
478
|
if (col.references) {
|
|
@@ -577,7 +577,7 @@ function toPascalCase(str) {
|
|
|
577
577
|
function escapeString(str) {
|
|
578
578
|
return str.replace(/'/g, "\\'").replace(/\\/g, '\\\\');
|
|
579
579
|
}
|
|
580
|
-
function formatDefaultValue(value) {
|
|
580
|
+
function formatDefaultValue(value, colType) {
|
|
581
581
|
const upper = value.toUpperCase().trim();
|
|
582
582
|
if (upper === 'NOW()' || upper === 'CURRENT_TIMESTAMP' ||
|
|
583
583
|
upper === 'CURRENT_DATE' || upper === 'CURRENT_TIME') {
|
|
@@ -592,7 +592,11 @@ function formatDefaultValue(value) {
|
|
|
592
592
|
if (upper === 'NULL') {
|
|
593
593
|
return 'null';
|
|
594
594
|
}
|
|
595
|
-
|
|
595
|
+
const isBigint = colType?.toLowerCase().startsWith('bigint');
|
|
596
|
+
if (/^-?\d+$/.test(value)) {
|
|
597
|
+
return isBigint ? `${value}n` : value;
|
|
598
|
+
}
|
|
599
|
+
if (/^-?\d+\.\d+$/.test(value)) {
|
|
596
600
|
return value;
|
|
597
601
|
}
|
|
598
602
|
if (value.includes('(')) {
|