@dbml/connector 3.8.0 → 3.9.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__/connector.test.ts +2 -2
- package/__tests__/connectors/postgres/expect-out-files/schema.json +208 -0
- package/dist/connectors/mssqlConnector.d.ts +1 -1
- package/dist/connectors/mssqlConnector.js +89 -61
- package/dist/connectors/postgresConnector.d.ts +1 -1
- package/dist/connectors/postgresConnector.js +9 -7
- package/package.json +2 -2
- package/src/connectors/mssqlConnector.ts +97 -69
- package/src/connectors/postgresConnector.ts +11 -9
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
1
|
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
3
|
import { scanDirNames } from '../jestHelpers.ts';
|
|
4
4
|
import { connector } from '../src/index.ts';
|
|
5
5
|
|
|
6
6
|
describe('@dbml/connector', () => {
|
|
7
|
-
const runTest = async (dirName) => {
|
|
7
|
+
const runTest = async (dirName: string) => {
|
|
8
8
|
process.chdir(dirName);
|
|
9
9
|
|
|
10
10
|
const connectionRaw = fs.readFileSync(path.join(dirName, './connection.json'), 'utf-8');
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"tables": [
|
|
3
|
+
{
|
|
4
|
+
"name": "table1",
|
|
5
|
+
"schemaName": "dbml_test",
|
|
6
|
+
"note": {
|
|
7
|
+
"value": ""
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"name": "table3",
|
|
12
|
+
"schemaName": "dbml_test",
|
|
13
|
+
"note": {
|
|
14
|
+
"value": ""
|
|
15
|
+
}
|
|
16
|
+
},
|
|
3
17
|
{
|
|
4
18
|
"name": "all_default_values",
|
|
5
19
|
"schemaName": "public",
|
|
@@ -49,6 +63,20 @@
|
|
|
49
63
|
"value": ""
|
|
50
64
|
}
|
|
51
65
|
},
|
|
66
|
+
{
|
|
67
|
+
"name": "table1",
|
|
68
|
+
"schemaName": "public",
|
|
69
|
+
"note": {
|
|
70
|
+
"value": ""
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"name": "table2",
|
|
75
|
+
"schemaName": "public",
|
|
76
|
+
"note": {
|
|
77
|
+
"value": ""
|
|
78
|
+
}
|
|
79
|
+
},
|
|
52
80
|
{
|
|
53
81
|
"name": "table_with_comments",
|
|
54
82
|
"schemaName": "public",
|
|
@@ -72,6 +100,62 @@
|
|
|
72
100
|
}
|
|
73
101
|
],
|
|
74
102
|
"fields": {
|
|
103
|
+
"dbml_test.table1": [
|
|
104
|
+
{
|
|
105
|
+
"name": "id",
|
|
106
|
+
"type": {
|
|
107
|
+
"type_name": "int4",
|
|
108
|
+
"schemaName": null
|
|
109
|
+
},
|
|
110
|
+
"dbdefault": null,
|
|
111
|
+
"not_null": true,
|
|
112
|
+
"increment": true,
|
|
113
|
+
"note": {
|
|
114
|
+
"value": ""
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"name": "status",
|
|
119
|
+
"type": {
|
|
120
|
+
"type_name": "enum_type1",
|
|
121
|
+
"schemaName": "dbml_test"
|
|
122
|
+
},
|
|
123
|
+
"dbdefault": null,
|
|
124
|
+
"not_null": false,
|
|
125
|
+
"increment": false,
|
|
126
|
+
"note": {
|
|
127
|
+
"value": ""
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
],
|
|
131
|
+
"dbml_test.table3": [
|
|
132
|
+
{
|
|
133
|
+
"name": "id",
|
|
134
|
+
"type": {
|
|
135
|
+
"type_name": "int4",
|
|
136
|
+
"schemaName": null
|
|
137
|
+
},
|
|
138
|
+
"dbdefault": null,
|
|
139
|
+
"not_null": true,
|
|
140
|
+
"increment": true,
|
|
141
|
+
"note": {
|
|
142
|
+
"value": ""
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"name": "status",
|
|
147
|
+
"type": {
|
|
148
|
+
"type_name": "enum_type3",
|
|
149
|
+
"schemaName": "dbml_test"
|
|
150
|
+
},
|
|
151
|
+
"dbdefault": null,
|
|
152
|
+
"not_null": false,
|
|
153
|
+
"increment": false,
|
|
154
|
+
"note": {
|
|
155
|
+
"value": ""
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
],
|
|
75
159
|
"public.all_default_values": [
|
|
76
160
|
{
|
|
77
161
|
"name": "id",
|
|
@@ -843,6 +927,62 @@
|
|
|
843
927
|
}
|
|
844
928
|
}
|
|
845
929
|
],
|
|
930
|
+
"public.table1": [
|
|
931
|
+
{
|
|
932
|
+
"name": "id",
|
|
933
|
+
"type": {
|
|
934
|
+
"type_name": "int4",
|
|
935
|
+
"schemaName": null
|
|
936
|
+
},
|
|
937
|
+
"dbdefault": null,
|
|
938
|
+
"not_null": true,
|
|
939
|
+
"increment": true,
|
|
940
|
+
"note": {
|
|
941
|
+
"value": ""
|
|
942
|
+
}
|
|
943
|
+
},
|
|
944
|
+
{
|
|
945
|
+
"name": "status",
|
|
946
|
+
"type": {
|
|
947
|
+
"type_name": "enum_type1",
|
|
948
|
+
"schemaName": "public"
|
|
949
|
+
},
|
|
950
|
+
"dbdefault": null,
|
|
951
|
+
"not_null": false,
|
|
952
|
+
"increment": false,
|
|
953
|
+
"note": {
|
|
954
|
+
"value": ""
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
],
|
|
958
|
+
"public.table2": [
|
|
959
|
+
{
|
|
960
|
+
"name": "id",
|
|
961
|
+
"type": {
|
|
962
|
+
"type_name": "int4",
|
|
963
|
+
"schemaName": null
|
|
964
|
+
},
|
|
965
|
+
"dbdefault": null,
|
|
966
|
+
"not_null": true,
|
|
967
|
+
"increment": true,
|
|
968
|
+
"note": {
|
|
969
|
+
"value": ""
|
|
970
|
+
}
|
|
971
|
+
},
|
|
972
|
+
{
|
|
973
|
+
"name": "status",
|
|
974
|
+
"type": {
|
|
975
|
+
"type_name": "enum_type2",
|
|
976
|
+
"schemaName": "public"
|
|
977
|
+
},
|
|
978
|
+
"dbdefault": null,
|
|
979
|
+
"not_null": false,
|
|
980
|
+
"increment": false,
|
|
981
|
+
"note": {
|
|
982
|
+
"value": ""
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
],
|
|
846
986
|
"public.table_with_comments": [
|
|
847
987
|
{
|
|
848
988
|
"name": "id",
|
|
@@ -1229,6 +1369,54 @@
|
|
|
1229
1369
|
}
|
|
1230
1370
|
],
|
|
1231
1371
|
"enums": [
|
|
1372
|
+
{
|
|
1373
|
+
"name": "enum_type1",
|
|
1374
|
+
"schemaName": "dbml_test",
|
|
1375
|
+
"values": [
|
|
1376
|
+
{
|
|
1377
|
+
"name": "value1"
|
|
1378
|
+
},
|
|
1379
|
+
{
|
|
1380
|
+
"name": "value2"
|
|
1381
|
+
}
|
|
1382
|
+
]
|
|
1383
|
+
},
|
|
1384
|
+
{
|
|
1385
|
+
"name": "enum_type3",
|
|
1386
|
+
"schemaName": "dbml_test",
|
|
1387
|
+
"values": [
|
|
1388
|
+
{
|
|
1389
|
+
"name": "value4"
|
|
1390
|
+
},
|
|
1391
|
+
{
|
|
1392
|
+
"name": "value5"
|
|
1393
|
+
}
|
|
1394
|
+
]
|
|
1395
|
+
},
|
|
1396
|
+
{
|
|
1397
|
+
"name": "enum_type1",
|
|
1398
|
+
"schemaName": "public",
|
|
1399
|
+
"values": [
|
|
1400
|
+
{
|
|
1401
|
+
"name": "value1"
|
|
1402
|
+
},
|
|
1403
|
+
{
|
|
1404
|
+
"name": "value2"
|
|
1405
|
+
}
|
|
1406
|
+
]
|
|
1407
|
+
},
|
|
1408
|
+
{
|
|
1409
|
+
"name": "enum_type2",
|
|
1410
|
+
"schemaName": "public",
|
|
1411
|
+
"values": [
|
|
1412
|
+
{
|
|
1413
|
+
"name": "value1"
|
|
1414
|
+
},
|
|
1415
|
+
{
|
|
1416
|
+
"name": "value3"
|
|
1417
|
+
}
|
|
1418
|
+
]
|
|
1419
|
+
},
|
|
1232
1420
|
{
|
|
1233
1421
|
"name": "gender_type",
|
|
1234
1422
|
"schemaName": "public",
|
|
@@ -1386,6 +1574,26 @@
|
|
|
1386
1574
|
"pk": true
|
|
1387
1575
|
}
|
|
1388
1576
|
},
|
|
1577
|
+
"public.table1": {
|
|
1578
|
+
"id": {
|
|
1579
|
+
"pk": true
|
|
1580
|
+
}
|
|
1581
|
+
},
|
|
1582
|
+
"dbml_test.table1": {
|
|
1583
|
+
"id": {
|
|
1584
|
+
"pk": true
|
|
1585
|
+
}
|
|
1586
|
+
},
|
|
1587
|
+
"public.table2": {
|
|
1588
|
+
"id": {
|
|
1589
|
+
"pk": true
|
|
1590
|
+
}
|
|
1591
|
+
},
|
|
1592
|
+
"dbml_test.table3": {
|
|
1593
|
+
"id": {
|
|
1594
|
+
"pk": true
|
|
1595
|
+
}
|
|
1596
|
+
},
|
|
1389
1597
|
"public.table_with_comments": {
|
|
1390
1598
|
"id": {
|
|
1391
1599
|
"pk": true
|
|
@@ -106,71 +106,99 @@ const generateTablesFieldsAndEnums = async (client, schemas) => {
|
|
|
106
106
|
const fields = {};
|
|
107
107
|
const enums = [];
|
|
108
108
|
const tablesAndFieldsSql = `
|
|
109
|
-
|
|
109
|
+
WITH tables_and_fields AS (
|
|
110
|
+
SELECT
|
|
111
|
+
s.name AS table_schema,
|
|
112
|
+
t.name AS table_name,
|
|
113
|
+
c.name AS column_name,
|
|
114
|
+
ty.name AS data_type,
|
|
115
|
+
c.max_length AS character_maximum_length,
|
|
116
|
+
c.precision AS numeric_precision,
|
|
117
|
+
c.scale AS numeric_scale,
|
|
118
|
+
c.is_identity AS identity_increment,
|
|
119
|
+
CASE
|
|
120
|
+
WHEN c.is_nullable = 1 THEN 'YES'
|
|
121
|
+
ELSE 'NO'
|
|
122
|
+
END AS is_nullable,
|
|
123
|
+
CASE
|
|
124
|
+
WHEN c.default_object_id = 0 THEN NULL
|
|
125
|
+
ELSE OBJECT_DEFINITION(c.default_object_id)
|
|
126
|
+
END AS column_default,
|
|
127
|
+
-- Fetching table comments
|
|
128
|
+
p.value AS table_comment,
|
|
129
|
+
ep.value AS column_comment
|
|
130
|
+
FROM
|
|
131
|
+
sys.tables t
|
|
132
|
+
JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
133
|
+
JOIN sys.columns c ON t.object_id = c.object_id
|
|
134
|
+
JOIN sys.types ty ON c.user_type_id = ty.user_type_id
|
|
135
|
+
LEFT JOIN sys.extended_properties p ON p.major_id = t.object_id
|
|
136
|
+
AND p.name = 'MS_Description'
|
|
137
|
+
AND p.minor_id = 0 -- Ensure minor_id is 0 for table comments
|
|
138
|
+
LEFT JOIN sys.extended_properties ep ON ep.major_id = c.object_id
|
|
139
|
+
AND ep.minor_id = c.column_id
|
|
140
|
+
AND ep.name = 'MS_Description'
|
|
141
|
+
WHERE
|
|
142
|
+
t.type = 'U' -- User-defined tables
|
|
143
|
+
),
|
|
144
|
+
constraints_with_nulls AS (
|
|
145
|
+
SELECT
|
|
146
|
+
tf.*,
|
|
147
|
+
cc.name AS check_constraint_name,
|
|
148
|
+
CASE
|
|
149
|
+
WHEN cc.definition LIKE '%[' + tf.column_name + ']%=''%' THEN cc.definition
|
|
150
|
+
ELSE NULL
|
|
151
|
+
END AS check_constraint_definition
|
|
152
|
+
FROM
|
|
153
|
+
tables_and_fields AS tf
|
|
154
|
+
LEFT JOIN sys.check_constraints cc
|
|
155
|
+
ON cc.parent_object_id = OBJECT_ID(tf.table_schema + '.' + tf.table_name)
|
|
156
|
+
AND cc.definition LIKE '%' + tf.column_name + '%' -- Ensure the constraint references the column
|
|
157
|
+
),
|
|
158
|
+
constraints_with_row_number AS (
|
|
159
|
+
SELECT
|
|
160
|
+
*,
|
|
161
|
+
ROW_NUMBER() OVER (
|
|
162
|
+
PARTITION BY table_schema, table_name, column_name
|
|
163
|
+
ORDER BY
|
|
164
|
+
CASE
|
|
165
|
+
WHEN check_constraint_definition IS NOT NULL THEN 1
|
|
166
|
+
ELSE 2
|
|
167
|
+
END,
|
|
168
|
+
check_constraint_name
|
|
169
|
+
) AS rn
|
|
170
|
+
FROM
|
|
171
|
+
constraints_with_nulls
|
|
172
|
+
)
|
|
110
173
|
SELECT
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
174
|
+
table_schema,
|
|
175
|
+
table_name,
|
|
176
|
+
column_name,
|
|
177
|
+
data_type,
|
|
178
|
+
character_maximum_length,
|
|
179
|
+
numeric_precision,
|
|
180
|
+
numeric_scale,
|
|
181
|
+
identity_increment,
|
|
182
|
+
is_nullable,
|
|
183
|
+
column_default,
|
|
184
|
+
table_comment,
|
|
185
|
+
column_comment,
|
|
186
|
+
check_constraint_name,
|
|
187
|
+
check_constraint_definition,
|
|
119
188
|
CASE
|
|
120
|
-
WHEN
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
WHEN c.default_object_id = 0 THEN NULL
|
|
125
|
-
ELSE OBJECT_DEFINITION(c.default_object_id)
|
|
126
|
-
END AS column_default,
|
|
127
|
-
-- Fetching table comments
|
|
128
|
-
p.value AS table_comment,
|
|
129
|
-
ep.value AS column_comment
|
|
189
|
+
WHEN column_default LIKE '((%))' THEN 'number'
|
|
190
|
+
WHEN column_default LIKE '(''%'')' THEN 'string'
|
|
191
|
+
ELSE 'expression'
|
|
192
|
+
END AS default_type
|
|
130
193
|
FROM
|
|
131
|
-
|
|
132
|
-
JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
133
|
-
JOIN sys.columns c ON t.object_id = c.object_id
|
|
134
|
-
JOIN sys.types ty ON c.user_type_id = ty.user_type_id
|
|
135
|
-
LEFT JOIN sys.extended_properties p ON p.major_id = t.object_id
|
|
136
|
-
AND p.name = 'MS_Description'
|
|
137
|
-
AND p.minor_id = 0 -- Ensure minor_id is 0 for table comments
|
|
138
|
-
LEFT JOIN sys.extended_properties ep ON ep.major_id = c.object_id
|
|
139
|
-
AND ep.minor_id = c.column_id
|
|
140
|
-
AND ep.name = 'MS_Description'
|
|
194
|
+
constraints_with_row_number
|
|
141
195
|
WHERE
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
tf.data_type,
|
|
149
|
-
tf.character_maximum_length,
|
|
150
|
-
tf.numeric_precision,
|
|
151
|
-
tf.numeric_scale,
|
|
152
|
-
tf.identity_increment,
|
|
153
|
-
tf.is_nullable,
|
|
154
|
-
tf.column_default,
|
|
155
|
-
tf.table_comment,
|
|
156
|
-
tf.column_comment,
|
|
157
|
-
cc.name AS check_constraint_name, -- Adding CHECK constraint name
|
|
158
|
-
cc.definition AS check_constraint_definition, -- Adding CHECK constraint definition
|
|
159
|
-
CASE
|
|
160
|
-
WHEN tf.column_default LIKE '((%))' THEN 'number'
|
|
161
|
-
WHEN tf.column_default LIKE '(''%'')' THEN 'string'
|
|
162
|
-
ELSE 'expression'
|
|
163
|
-
END AS default_type
|
|
164
|
-
FROM
|
|
165
|
-
tables_and_fields AS tf
|
|
166
|
-
LEFT JOIN sys.check_constraints cc
|
|
167
|
-
ON cc.parent_object_id = OBJECT_ID(tf.table_schema + '.' + tf.table_name)
|
|
168
|
-
AND cc.definition LIKE '%' + tf.column_name + '%' -- Ensure the constraint references the column
|
|
169
|
-
${(0, parseSchema_1.buildSchemaQuery)('tf.table_schema', schemas, 'WHERE')}
|
|
170
|
-
ORDER BY
|
|
171
|
-
tf.table_schema,
|
|
172
|
-
tf.table_name,
|
|
173
|
-
tf.column_name;
|
|
196
|
+
rn = 1
|
|
197
|
+
${(0, parseSchema_1.buildSchemaQuery)('table_schema', schemas, 'WHERE')}
|
|
198
|
+
ORDER BY
|
|
199
|
+
table_schema,
|
|
200
|
+
table_name,
|
|
201
|
+
column_name;
|
|
174
202
|
`;
|
|
175
203
|
const tablesAndFieldsResult = await client.query(tablesAndFieldsSql);
|
|
176
204
|
const tables = tablesAndFieldsResult.recordset.reduce((acc, row) => {
|
|
@@ -83,7 +83,7 @@ const generateTablesAndFields = async (client, schemas) => {
|
|
|
83
83
|
const fields = {};
|
|
84
84
|
const tablesAndFieldsSql = `
|
|
85
85
|
WITH comments AS (
|
|
86
|
-
SELECT
|
|
86
|
+
SELECT DISTINCT ON (pc.relname, pn.nspname, pa.attname)
|
|
87
87
|
pc.relname AS table_name,
|
|
88
88
|
pn.nspname AS table_schema,
|
|
89
89
|
pa.attname AS column_name,
|
|
@@ -113,6 +113,7 @@ const generateTablesAndFields = async (client, schemas) => {
|
|
|
113
113
|
c.identity_increment,
|
|
114
114
|
c.is_nullable,
|
|
115
115
|
c.column_default,
|
|
116
|
+
c.ordinal_position,
|
|
116
117
|
CASE
|
|
117
118
|
WHEN c.column_default IS NULL THEN NULL
|
|
118
119
|
WHEN c.column_default LIKE 'nextval(%' THEN 'increment'
|
|
@@ -121,8 +122,8 @@ const generateTablesAndFields = async (client, schemas) => {
|
|
|
121
122
|
WHEN c.column_default ~ '^-?[0-9]+(.[0-9]+)?$' THEN 'number'
|
|
122
123
|
ELSE 'expression'
|
|
123
124
|
END AS default_type,
|
|
124
|
-
(SELECT description FROM comments WHERE table_name = t.table_name AND table_schema = t.table_schema AND column_name IS NULL) AS table_comment,
|
|
125
|
-
(SELECT description FROM comments WHERE table_name = t.table_name AND table_schema = t.table_schema AND column_name = c.column_name) AS column_comment
|
|
125
|
+
(SELECT description FROM comments WHERE table_name = t.table_name AND table_schema = t.table_schema AND column_name IS NULL LIMIT 1) AS table_comment,
|
|
126
|
+
(SELECT description FROM comments WHERE table_name = t.table_name AND table_schema = t.table_schema AND column_name = c.column_name LIMIT 1) AS column_comment
|
|
126
127
|
FROM
|
|
127
128
|
information_schema.columns c
|
|
128
129
|
JOIN
|
|
@@ -355,7 +356,7 @@ const generateIndexes = async (client, schemas) => {
|
|
|
355
356
|
};
|
|
356
357
|
const generateRawEnums = async (client, schemas) => {
|
|
357
358
|
const enumListSql = `
|
|
358
|
-
SELECT
|
|
359
|
+
SELECT DISTINCT
|
|
359
360
|
n.nspname AS schema_name,
|
|
360
361
|
t.typname AS enum_type,
|
|
361
362
|
e.enumlabel AS enum_value,
|
|
@@ -376,14 +377,15 @@ const generateRawEnums = async (client, schemas) => {
|
|
|
376
377
|
const enumListResult = await client.query(enumListSql);
|
|
377
378
|
const enums = enumListResult.rows.reduce((acc, row) => {
|
|
378
379
|
const { schema_name, enum_type, enum_value } = row;
|
|
379
|
-
|
|
380
|
-
|
|
380
|
+
const key = `${schema_name}.${enum_type}`;
|
|
381
|
+
if (!acc[key]) {
|
|
382
|
+
acc[key] = {
|
|
381
383
|
name: enum_type,
|
|
382
384
|
schemaName: schema_name,
|
|
383
385
|
values: [],
|
|
384
386
|
};
|
|
385
387
|
}
|
|
386
|
-
acc[
|
|
388
|
+
acc[key].values.push({
|
|
387
389
|
name: enum_value,
|
|
388
390
|
});
|
|
389
391
|
return acc;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dbml/connector",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.9.0",
|
|
4
4
|
"description": "This package was created to fetch the schema JSON from many kind of databases.",
|
|
5
5
|
"author": "huy.phung.sw@gmail.com",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,5 +40,5 @@
|
|
|
40
40
|
"engines": {
|
|
41
41
|
"node": ">=18"
|
|
42
42
|
},
|
|
43
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "d40aaa3827853d17fa385aa27ae1e099c9234e4b"
|
|
44
44
|
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
/* eslint-disable camelcase */
|
|
2
2
|
import sql from 'mssql';
|
|
3
|
+
import { buildSchemaQuery, parseConnectionString } from '../utils/parseSchema';
|
|
3
4
|
import {
|
|
4
|
-
|
|
5
|
+
DatabaseSchema,
|
|
6
|
+
DefaultInfo,
|
|
5
7
|
DefaultType,
|
|
6
|
-
FieldsDictionary,
|
|
7
8
|
Enum,
|
|
9
|
+
EnumValue,
|
|
10
|
+
Field,
|
|
11
|
+
FieldsDictionary,
|
|
12
|
+
IndexesDictionary,
|
|
8
13
|
Ref,
|
|
9
14
|
RefEndpoint,
|
|
10
15
|
Table,
|
|
11
|
-
IndexesDictionary,
|
|
12
|
-
EnumValue,
|
|
13
16
|
TableConstraintsDictionary,
|
|
14
|
-
DatabaseSchema,
|
|
15
|
-
DefaultInfo,
|
|
16
17
|
} from './types';
|
|
17
|
-
import { buildSchemaQuery, parseConnectionString } from '../utils/parseSchema';
|
|
18
18
|
|
|
19
19
|
const MSSQL_DATE_TYPES = [
|
|
20
20
|
'date',
|
|
@@ -141,71 +141,99 @@ const generateTablesFieldsAndEnums = async (client: sql.ConnectionPool, schemas:
|
|
|
141
141
|
const fields: FieldsDictionary = {};
|
|
142
142
|
const enums: Enum[] = [];
|
|
143
143
|
const tablesAndFieldsSql = `
|
|
144
|
-
|
|
144
|
+
WITH tables_and_fields AS (
|
|
145
|
+
SELECT
|
|
146
|
+
s.name AS table_schema,
|
|
147
|
+
t.name AS table_name,
|
|
148
|
+
c.name AS column_name,
|
|
149
|
+
ty.name AS data_type,
|
|
150
|
+
c.max_length AS character_maximum_length,
|
|
151
|
+
c.precision AS numeric_precision,
|
|
152
|
+
c.scale AS numeric_scale,
|
|
153
|
+
c.is_identity AS identity_increment,
|
|
154
|
+
CASE
|
|
155
|
+
WHEN c.is_nullable = 1 THEN 'YES'
|
|
156
|
+
ELSE 'NO'
|
|
157
|
+
END AS is_nullable,
|
|
158
|
+
CASE
|
|
159
|
+
WHEN c.default_object_id = 0 THEN NULL
|
|
160
|
+
ELSE OBJECT_DEFINITION(c.default_object_id)
|
|
161
|
+
END AS column_default,
|
|
162
|
+
-- Fetching table comments
|
|
163
|
+
p.value AS table_comment,
|
|
164
|
+
ep.value AS column_comment
|
|
165
|
+
FROM
|
|
166
|
+
sys.tables t
|
|
167
|
+
JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
168
|
+
JOIN sys.columns c ON t.object_id = c.object_id
|
|
169
|
+
JOIN sys.types ty ON c.user_type_id = ty.user_type_id
|
|
170
|
+
LEFT JOIN sys.extended_properties p ON p.major_id = t.object_id
|
|
171
|
+
AND p.name = 'MS_Description'
|
|
172
|
+
AND p.minor_id = 0 -- Ensure minor_id is 0 for table comments
|
|
173
|
+
LEFT JOIN sys.extended_properties ep ON ep.major_id = c.object_id
|
|
174
|
+
AND ep.minor_id = c.column_id
|
|
175
|
+
AND ep.name = 'MS_Description'
|
|
176
|
+
WHERE
|
|
177
|
+
t.type = 'U' -- User-defined tables
|
|
178
|
+
),
|
|
179
|
+
constraints_with_nulls AS (
|
|
180
|
+
SELECT
|
|
181
|
+
tf.*,
|
|
182
|
+
cc.name AS check_constraint_name,
|
|
183
|
+
CASE
|
|
184
|
+
WHEN cc.definition LIKE '%[' + tf.column_name + ']%=''%' THEN cc.definition
|
|
185
|
+
ELSE NULL
|
|
186
|
+
END AS check_constraint_definition
|
|
187
|
+
FROM
|
|
188
|
+
tables_and_fields AS tf
|
|
189
|
+
LEFT JOIN sys.check_constraints cc
|
|
190
|
+
ON cc.parent_object_id = OBJECT_ID(tf.table_schema + '.' + tf.table_name)
|
|
191
|
+
AND cc.definition LIKE '%' + tf.column_name + '%' -- Ensure the constraint references the column
|
|
192
|
+
),
|
|
193
|
+
constraints_with_row_number AS (
|
|
194
|
+
SELECT
|
|
195
|
+
*,
|
|
196
|
+
ROW_NUMBER() OVER (
|
|
197
|
+
PARTITION BY table_schema, table_name, column_name
|
|
198
|
+
ORDER BY
|
|
199
|
+
CASE
|
|
200
|
+
WHEN check_constraint_definition IS NOT NULL THEN 1
|
|
201
|
+
ELSE 2
|
|
202
|
+
END,
|
|
203
|
+
check_constraint_name
|
|
204
|
+
) AS rn
|
|
205
|
+
FROM
|
|
206
|
+
constraints_with_nulls
|
|
207
|
+
)
|
|
145
208
|
SELECT
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
209
|
+
table_schema,
|
|
210
|
+
table_name,
|
|
211
|
+
column_name,
|
|
212
|
+
data_type,
|
|
213
|
+
character_maximum_length,
|
|
214
|
+
numeric_precision,
|
|
215
|
+
numeric_scale,
|
|
216
|
+
identity_increment,
|
|
217
|
+
is_nullable,
|
|
218
|
+
column_default,
|
|
219
|
+
table_comment,
|
|
220
|
+
column_comment,
|
|
221
|
+
check_constraint_name,
|
|
222
|
+
check_constraint_definition,
|
|
158
223
|
CASE
|
|
159
|
-
WHEN
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
p.value AS table_comment,
|
|
164
|
-
ep.value AS column_comment
|
|
224
|
+
WHEN column_default LIKE '((%))' THEN 'number'
|
|
225
|
+
WHEN column_default LIKE '(''%'')' THEN 'string'
|
|
226
|
+
ELSE 'expression'
|
|
227
|
+
END AS default_type
|
|
165
228
|
FROM
|
|
166
|
-
|
|
167
|
-
JOIN sys.schemas s ON t.schema_id = s.schema_id
|
|
168
|
-
JOIN sys.columns c ON t.object_id = c.object_id
|
|
169
|
-
JOIN sys.types ty ON c.user_type_id = ty.user_type_id
|
|
170
|
-
LEFT JOIN sys.extended_properties p ON p.major_id = t.object_id
|
|
171
|
-
AND p.name = 'MS_Description'
|
|
172
|
-
AND p.minor_id = 0 -- Ensure minor_id is 0 for table comments
|
|
173
|
-
LEFT JOIN sys.extended_properties ep ON ep.major_id = c.object_id
|
|
174
|
-
AND ep.minor_id = c.column_id
|
|
175
|
-
AND ep.name = 'MS_Description'
|
|
229
|
+
constraints_with_row_number
|
|
176
230
|
WHERE
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
tf.data_type,
|
|
184
|
-
tf.character_maximum_length,
|
|
185
|
-
tf.numeric_precision,
|
|
186
|
-
tf.numeric_scale,
|
|
187
|
-
tf.identity_increment,
|
|
188
|
-
tf.is_nullable,
|
|
189
|
-
tf.column_default,
|
|
190
|
-
tf.table_comment,
|
|
191
|
-
tf.column_comment,
|
|
192
|
-
cc.name AS check_constraint_name, -- Adding CHECK constraint name
|
|
193
|
-
cc.definition AS check_constraint_definition, -- Adding CHECK constraint definition
|
|
194
|
-
CASE
|
|
195
|
-
WHEN tf.column_default LIKE '((%))' THEN 'number'
|
|
196
|
-
WHEN tf.column_default LIKE '(''%'')' THEN 'string'
|
|
197
|
-
ELSE 'expression'
|
|
198
|
-
END AS default_type
|
|
199
|
-
FROM
|
|
200
|
-
tables_and_fields AS tf
|
|
201
|
-
LEFT JOIN sys.check_constraints cc
|
|
202
|
-
ON cc.parent_object_id = OBJECT_ID(tf.table_schema + '.' + tf.table_name)
|
|
203
|
-
AND cc.definition LIKE '%' + tf.column_name + '%' -- Ensure the constraint references the column
|
|
204
|
-
${buildSchemaQuery('tf.table_schema', schemas, 'WHERE')}
|
|
205
|
-
ORDER BY
|
|
206
|
-
tf.table_schema,
|
|
207
|
-
tf.table_name,
|
|
208
|
-
tf.column_name;
|
|
231
|
+
rn = 1
|
|
232
|
+
${buildSchemaQuery('table_schema', schemas, 'WHERE')}
|
|
233
|
+
ORDER BY
|
|
234
|
+
table_schema,
|
|
235
|
+
table_name,
|
|
236
|
+
column_name;
|
|
209
237
|
`;
|
|
210
238
|
|
|
211
239
|
const tablesAndFieldsResult = await client.query(tablesAndFieldsSql);
|
|
@@ -521,5 +549,5 @@ const fetchSchemaJson = async (connection: string): Promise<DatabaseSchema> => {
|
|
|
521
549
|
};
|
|
522
550
|
|
|
523
551
|
export {
|
|
524
|
-
fetchSchemaJson
|
|
552
|
+
fetchSchemaJson
|
|
525
553
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* eslint-disable camelcase */
|
|
2
2
|
import { Client } from 'pg';
|
|
3
|
+
import { buildSchemaQuery, parseConnectionString } from '../utils/parseSchema';
|
|
3
4
|
import {
|
|
4
5
|
DatabaseSchema,
|
|
5
6
|
DefaultInfo,
|
|
@@ -13,7 +14,6 @@ import {
|
|
|
13
14
|
Table,
|
|
14
15
|
TableConstraintsDictionary,
|
|
15
16
|
} from './types';
|
|
16
|
-
import { parseConnectionString, buildSchemaQuery } from '../utils/parseSchema';
|
|
17
17
|
|
|
18
18
|
const getValidatedClient = async (connection: string): Promise<Client> => {
|
|
19
19
|
const client = new Client(connection);
|
|
@@ -121,7 +121,7 @@ const generateTablesAndFields = async (client: Client, schemas: string[]): Promi
|
|
|
121
121
|
const fields: FieldsDictionary = {};
|
|
122
122
|
const tablesAndFieldsSql = `
|
|
123
123
|
WITH comments AS (
|
|
124
|
-
SELECT
|
|
124
|
+
SELECT DISTINCT ON (pc.relname, pn.nspname, pa.attname)
|
|
125
125
|
pc.relname AS table_name,
|
|
126
126
|
pn.nspname AS table_schema,
|
|
127
127
|
pa.attname AS column_name,
|
|
@@ -151,6 +151,7 @@ const generateTablesAndFields = async (client: Client, schemas: string[]): Promi
|
|
|
151
151
|
c.identity_increment,
|
|
152
152
|
c.is_nullable,
|
|
153
153
|
c.column_default,
|
|
154
|
+
c.ordinal_position,
|
|
154
155
|
CASE
|
|
155
156
|
WHEN c.column_default IS NULL THEN NULL
|
|
156
157
|
WHEN c.column_default LIKE 'nextval(%' THEN 'increment'
|
|
@@ -159,8 +160,8 @@ const generateTablesAndFields = async (client: Client, schemas: string[]): Promi
|
|
|
159
160
|
WHEN c.column_default ~ '^-?[0-9]+(.[0-9]+)?$' THEN 'number'
|
|
160
161
|
ELSE 'expression'
|
|
161
162
|
END AS default_type,
|
|
162
|
-
(SELECT description FROM comments WHERE table_name = t.table_name AND table_schema = t.table_schema AND column_name IS NULL) AS table_comment,
|
|
163
|
-
(SELECT description FROM comments WHERE table_name = t.table_name AND table_schema = t.table_schema AND column_name = c.column_name) AS column_comment
|
|
163
|
+
(SELECT description FROM comments WHERE table_name = t.table_name AND table_schema = t.table_schema AND column_name IS NULL LIMIT 1) AS table_comment,
|
|
164
|
+
(SELECT description FROM comments WHERE table_name = t.table_name AND table_schema = t.table_schema AND column_name = c.column_name LIMIT 1) AS column_comment
|
|
164
165
|
FROM
|
|
165
166
|
information_schema.columns c
|
|
166
167
|
JOIN
|
|
@@ -433,7 +434,7 @@ const generateIndexes = async (client: Client, schemas: string[]) => {
|
|
|
433
434
|
|
|
434
435
|
const generateRawEnums = async (client: Client, schemas: string[]): Promise<Enum[]> => {
|
|
435
436
|
const enumListSql = `
|
|
436
|
-
SELECT
|
|
437
|
+
SELECT DISTINCT
|
|
437
438
|
n.nspname AS schema_name,
|
|
438
439
|
t.typname AS enum_type,
|
|
439
440
|
e.enumlabel AS enum_value,
|
|
@@ -454,15 +455,16 @@ const generateRawEnums = async (client: Client, schemas: string[]): Promise<Enum
|
|
|
454
455
|
const enumListResult = await client.query(enumListSql);
|
|
455
456
|
const enums = enumListResult.rows.reduce((acc, row) => {
|
|
456
457
|
const { schema_name, enum_type, enum_value } = row;
|
|
458
|
+
const key = `${schema_name}.${enum_type}`;
|
|
457
459
|
|
|
458
|
-
if (!acc[
|
|
459
|
-
acc[
|
|
460
|
+
if (!acc[key]) {
|
|
461
|
+
acc[key] = {
|
|
460
462
|
name: enum_type,
|
|
461
463
|
schemaName: schema_name,
|
|
462
464
|
values: [],
|
|
463
465
|
};
|
|
464
466
|
}
|
|
465
|
-
acc[
|
|
467
|
+
acc[key].values.push({
|
|
466
468
|
name: enum_value,
|
|
467
469
|
});
|
|
468
470
|
return acc;
|
|
@@ -504,5 +506,5 @@ const fetchSchemaJson = async (connection: string): Promise<DatabaseSchema> => {
|
|
|
504
506
|
};
|
|
505
507
|
|
|
506
508
|
export {
|
|
507
|
-
fetchSchemaJson
|
|
509
|
+
fetchSchemaJson
|
|
508
510
|
};
|