typesql-cli 0.19.2 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/cli.js +51 -64
- package/cli.js.map +1 -1
- package/code-generator.d.ts +1 -1
- package/code-generator.d.ts.map +1 -1
- package/code-generator.js +4 -3
- package/code-generator.js.map +1 -1
- package/code-generator2.d.ts.map +1 -1
- package/code-generator2.js +151 -115
- package/code-generator2.js.map +1 -1
- package/codegen/code-generator.d.ts +13 -0
- package/codegen/code-generator.d.ts.map +1 -0
- package/codegen/code-generator.js +106 -0
- package/codegen/code-generator.js.map +1 -0
- package/codegen/code-generator2.d.ts +10 -0
- package/codegen/code-generator2.d.ts.map +1 -0
- package/codegen/code-generator2.js +917 -0
- package/codegen/code-generator2.js.map +1 -0
- package/codegen/codegen-util.d.ts +1 -0
- package/codegen/codegen-util.d.ts.map +1 -0
- package/codegen/codegen-util.js +2 -0
- package/codegen/codegen-util.js.map +1 -0
- package/codegen/generic/codegen-util.d.ts +8 -0
- package/codegen/generic/codegen-util.d.ts.map +1 -0
- package/codegen/generic/codegen-util.js +89 -0
- package/codegen/generic/codegen-util.js.map +1 -0
- package/codegen/mysql2.d.ts +40 -0
- package/codegen/mysql2.d.ts.map +1 -0
- package/codegen/mysql2.js +667 -0
- package/codegen/mysql2.js.map +1 -0
- package/codegen/pg.d.ts +9 -0
- package/codegen/pg.d.ts.map +1 -0
- package/codegen/pg.js +760 -0
- package/codegen/pg.js.map +1 -0
- package/codegen/postgres-pg.d.ts +10 -0
- package/codegen/postgres-pg.d.ts.map +1 -0
- package/codegen/postgres-pg.js +917 -0
- package/codegen/postgres-pg.js.map +1 -0
- package/codegen/shared/codegen-util.d.ts +28 -0
- package/codegen/shared/codegen-util.d.ts.map +1 -0
- package/codegen/shared/codegen-util.js +303 -0
- package/codegen/shared/codegen-util.js.map +1 -0
- package/codegen/sqlite-code-generator.d.ts +15 -0
- package/codegen/sqlite-code-generator.d.ts.map +1 -0
- package/codegen/sqlite-code-generator.js +1049 -0
- package/codegen/sqlite-code-generator.js.map +1 -0
- package/codegen/sqlite.d.ts +13 -0
- package/codegen/sqlite.d.ts.map +1 -0
- package/codegen/sqlite.js +893 -0
- package/codegen/sqlite.js.map +1 -0
- package/describe-query.d.ts.map +1 -1
- package/describe-query.js +45 -32
- package/describe-query.js.map +1 -1
- package/dialects/postgres.d.ts +4 -1
- package/dialects/postgres.d.ts.map +1 -1
- package/dialects/postgres.js +4 -2
- package/dialects/postgres.js.map +1 -1
- package/drivers/sqlite.d.ts +4 -1
- package/drivers/sqlite.d.ts.map +1 -1
- package/drivers/sqlite.js +4 -1
- package/drivers/sqlite.js.map +1 -1
- package/drivers/types.d.ts +3 -1
- package/drivers/types.d.ts.map +1 -1
- package/generic/codegen-util.d.ts +8 -0
- package/generic/codegen-util.d.ts.map +1 -0
- package/generic/codegen-util.js +89 -0
- package/generic/codegen-util.js.map +1 -0
- package/load-config.d.ts +6 -0
- package/load-config.d.ts.map +1 -1
- package/load-config.js +65 -1
- package/load-config.js.map +1 -1
- package/mysql-mapping.d.ts +4 -1
- package/mysql-mapping.d.ts.map +1 -1
- package/mysql-mapping.js +5 -3
- package/mysql-mapping.js.map +1 -1
- package/mysql-query-analyzer/infer-column-nullability.js +1 -1
- package/mysql-query-analyzer/infer-column-nullability.js.map +1 -1
- package/mysql-query-analyzer/parse.d.ts.map +1 -1
- package/mysql-query-analyzer/parse.js +3 -2
- package/mysql-query-analyzer/parse.js.map +1 -1
- package/package.json +2 -1
- package/postgres-pg.d.ts +10 -0
- package/postgres-pg.d.ts.map +1 -0
- package/postgres-pg.js +917 -0
- package/postgres-pg.js.map +1 -0
- package/postgres-query-analyzer/describe.d.ts +1 -1
- package/postgres-query-analyzer/describe.d.ts.map +1 -1
- package/postgres-query-analyzer/describe.js +77 -48
- package/postgres-query-analyzer/describe.js.map +1 -1
- package/postgres-query-analyzer/traverse.d.ts +1 -0
- package/postgres-query-analyzer/traverse.d.ts.map +1 -1
- package/postgres-query-analyzer/traverse.js +90 -28
- package/postgres-query-analyzer/traverse.js.map +1 -1
- package/postgres-query-analyzer/util.d.ts +9 -0
- package/postgres-query-analyzer/util.d.ts.map +1 -0
- package/postgres-query-analyzer/util.js +58 -0
- package/postgres-query-analyzer/util.js.map +1 -0
- package/rescript.d.ts +1 -0
- package/rescript.d.ts.map +1 -0
- package/rescript.js +2 -0
- package/rescript.js.map +1 -0
- package/sqlite-query-analyzer/parser.js +3 -3
- package/sqlite-query-analyzer/parser.js.map +1 -1
- package/sqlite-query-analyzer/replace-list-params.d.ts.map +1 -1
- package/sqlite-query-analyzer/replace-list-params.js.map +1 -1
- package/sqlite-query-analyzer/sqlite-code-generator.d.ts +15 -0
- package/sqlite-query-analyzer/sqlite-code-generator.d.ts.map +1 -0
- package/sqlite-query-analyzer/sqlite-code-generator.js +1049 -0
- package/sqlite-query-analyzer/sqlite-code-generator.js.map +1 -0
- package/sqlite-query-analyzer/traverse.js +1 -1
- package/sqlite-query-analyzer/traverse.js.map +1 -1
- package/sqlite.d.ts +43 -0
- package/sqlite.d.ts.map +1 -0
- package/sqlite.js +755 -0
- package/sqlite.js.map +1 -0
- package/ts-dynamic-query-descriptor.d.ts.map +1 -1
- package/ts-dynamic-query-descriptor.js +2 -1
- package/ts-dynamic-query-descriptor.js.map +1 -1
- package/ts-nested-descriptor.d.ts.map +1 -1
- package/ts-nested-descriptor.js +1 -1
- package/ts-nested-descriptor.js.map +1 -1
- package/types.d.ts +9 -1
- package/types.d.ts.map +1 -1
@@ -0,0 +1,917 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.generateCode = generateCode;
|
7
|
+
exports.createCodeBlockWriter = createCodeBlockWriter;
|
8
|
+
exports.generateCrud = generateCrud;
|
9
|
+
exports.mapPostgresColumnSchemaToTsFieldDescriptor = mapPostgresColumnSchemaToTsFieldDescriptor;
|
10
|
+
const code_block_writer_1 = __importDefault(require("code-block-writer"));
|
11
|
+
const sqlite_1 = require("./sqlite");
|
12
|
+
const describe_1 = require("../postgres-query-analyzer/describe");
|
13
|
+
const postgres_1 = require("../dialects/postgres");
|
14
|
+
const neverthrow_1 = require("neverthrow");
|
15
|
+
const code_generator_1 = require("../sqlite-query-analyzer/code-generator");
|
16
|
+
const ts_nested_descriptor_1 = require("../ts-nested-descriptor");
|
17
|
+
const node_os_1 = require("node:os");
|
18
|
+
const codegen_util_1 = require("./shared/codegen-util");
|
19
|
+
function generateCode(client, sql, queryName, schemaInfo) {
|
20
|
+
if (isEmptySql(sql)) {
|
21
|
+
return (0, neverthrow_1.okAsync)('');
|
22
|
+
}
|
23
|
+
return _describeQuery(client, sql, schemaInfo)
|
24
|
+
.map(schemaDef => generateTsCode(sql, queryName, schemaDef, client.type));
|
25
|
+
}
|
26
|
+
function isEmptySql(sql) {
|
27
|
+
if (sql.trim() === '') {
|
28
|
+
return true;
|
29
|
+
}
|
30
|
+
const lines = sql.split('\n');
|
31
|
+
return lines.every(line => line.trim() === '' || line.trim().startsWith('//'));
|
32
|
+
}
|
33
|
+
function _describeQuery(databaseClient, sql, dbSchema) {
|
34
|
+
return (0, describe_1.describeQuery)(databaseClient.client, sql, dbSchema);
|
35
|
+
}
|
36
|
+
function createCodeBlockWriter() {
|
37
|
+
const writer = new code_block_writer_1.default({
|
38
|
+
useTabs: true,
|
39
|
+
newLine: node_os_1.EOL
|
40
|
+
});
|
41
|
+
return writer;
|
42
|
+
}
|
43
|
+
function generateTsCode(sqlOld, queryName, schemaDef, client, isCrud = false) {
|
44
|
+
const { sql } = schemaDef;
|
45
|
+
const writer = createCodeBlockWriter();
|
46
|
+
const camelCaseName = (0, sqlite_1.convertToCamelCaseName)(queryName);
|
47
|
+
const capitalizedName = (0, sqlite_1.capitalize)(camelCaseName);
|
48
|
+
const dataTypeName = `${capitalizedName}Data`;
|
49
|
+
const resultTypeName = `${capitalizedName}Result`;
|
50
|
+
const paramsTypeName = `${capitalizedName}Params`;
|
51
|
+
const orderByTypeName = `${capitalizedName}OrderBy`;
|
52
|
+
const dynamicParamsTypeName = `${capitalizedName}DynamicParams`;
|
53
|
+
const selectColumnsTypeName = `${capitalizedName}Select`;
|
54
|
+
const whereTypeName = `${capitalizedName}Where`;
|
55
|
+
const tsDescriptor = createTsDescriptor(capitalizedName, schemaDef);
|
56
|
+
const uniqueParams = (0, sqlite_1.removeDuplicatedParameters2)(tsDescriptor.parameters);
|
57
|
+
const generateOrderBy = tsDescriptor.orderByColumns != null && tsDescriptor.orderByColumns.length > 0;
|
58
|
+
const codeWriter = getCodeWriter(client);
|
59
|
+
codeWriter.writeImports(writer, schemaDef.queryType);
|
60
|
+
if (tsDescriptor.dynamicQuery2) {
|
61
|
+
writer.writeLine(`import { EOL } from 'os';`);
|
62
|
+
}
|
63
|
+
const uniqueDataParams = (0, sqlite_1.removeDuplicatedParameters2)(tsDescriptor.data || []);
|
64
|
+
if (uniqueDataParams.length > 0) {
|
65
|
+
writer.blankLine();
|
66
|
+
writeDataType(writer, dataTypeName, uniqueDataParams);
|
67
|
+
}
|
68
|
+
if (uniqueParams.length > 0 || generateOrderBy) {
|
69
|
+
writer.blankLine();
|
70
|
+
writeParamsType(writer, paramsTypeName, uniqueParams, generateOrderBy, orderByTypeName);
|
71
|
+
}
|
72
|
+
if (schemaDef.queryType !== 'Copy') {
|
73
|
+
writer.blankLine();
|
74
|
+
writeResultType(writer, resultTypeName, tsDescriptor.columns);
|
75
|
+
const flatten = schemaDef.columns.flatMap(col => flattenJsonTypes(createJsonType(capitalizedName, col.name), col.type));
|
76
|
+
flatten.forEach(type => {
|
77
|
+
writer.blankLine();
|
78
|
+
writeJsonTypes(writer, type.typeName, type.type);
|
79
|
+
});
|
80
|
+
}
|
81
|
+
const dynamicQueryInfo = tsDescriptor.dynamicQuery2;
|
82
|
+
if (dynamicQueryInfo) {
|
83
|
+
writer.blankLine();
|
84
|
+
writer.write(`export type ${dynamicParamsTypeName} = `).block(() => {
|
85
|
+
writer.writeLine(`select?: ${selectColumnsTypeName};`);
|
86
|
+
if (tsDescriptor.parameters.length > 0) {
|
87
|
+
writer.writeLine(`params: ${paramsTypeName};`);
|
88
|
+
}
|
89
|
+
writer.writeLine(`where?: ${whereTypeName}[];`);
|
90
|
+
// if (orderByField) {
|
91
|
+
// writer.writeLine(`${orderByField};`);
|
92
|
+
// }
|
93
|
+
});
|
94
|
+
writer.blankLine();
|
95
|
+
writer.write(`export type ${selectColumnsTypeName} =`).block(() => {
|
96
|
+
tsDescriptor.columns.forEach((tsField) => {
|
97
|
+
writer.writeLine(`${tsField.name}?: boolean;`);
|
98
|
+
});
|
99
|
+
});
|
100
|
+
writer.blankLine();
|
101
|
+
writer.write('const selectFragments = ').inlineBlock(() => {
|
102
|
+
dynamicQueryInfo.select.forEach((fragment, index) => {
|
103
|
+
const field = tsDescriptor.columns[index].name;
|
104
|
+
writer.writeLine(`${field}: \`${fragment.fragmentWitoutAlias}\`,`);
|
105
|
+
});
|
106
|
+
});
|
107
|
+
writer.write(' as const;');
|
108
|
+
writer.blankLine();
|
109
|
+
(0, codegen_util_1.writeDynamicQueryOperators)(writer, whereTypeName, tsDescriptor.columns);
|
110
|
+
writer.blankLine();
|
111
|
+
let functionArguments = 'client: pg.Client | pg.Pool | pg.PoolClient';
|
112
|
+
// if (params.data.length > 0) {
|
113
|
+
// functionParams += `, data: ${dataType}`;
|
114
|
+
// }
|
115
|
+
functionArguments += `, params?: ${dynamicParamsTypeName}`;
|
116
|
+
writer.write(`export async function ${camelCaseName}(${functionArguments}): Promise<${resultTypeName}[]>`).block(() => {
|
117
|
+
// if (orderByField != null) {
|
118
|
+
// writer.writeLine('const orderBy = orderByToObject(params.orderBy);');
|
119
|
+
// }
|
120
|
+
writer.blankLine();
|
121
|
+
writer.writeLine('const { sql, paramsValues, selectedFields } = buildSql(params);');
|
122
|
+
writer.write(`return client.query({ text: sql, rowMode: 'array', values: paramsValues })`).newLine();
|
123
|
+
writer.indent().write(`.then(res => res.rows.map(row => mapArrayTo${resultTypeName}(row, selectedFields)));`);
|
124
|
+
});
|
125
|
+
writer.blankLine();
|
126
|
+
writer.write(`function buildSql(params?: ${dynamicParamsTypeName})`).block(() => {
|
127
|
+
writer.writeLine(`const isSelected = (field: keyof ${selectColumnsTypeName}) =>`);
|
128
|
+
writer.indent().write('params?.select == null || params.select[field] === true;').newLine();
|
129
|
+
writer.blankLine();
|
130
|
+
writer.writeLine('const selectedSqlFragments: string[] = [];');
|
131
|
+
writer.writeLine(`const selectedFields: (keyof ${resultTypeName})[] = [];`);
|
132
|
+
writer.writeLine('const paramsValues: any[] = [];');
|
133
|
+
writer.blankLine();
|
134
|
+
writer.writeLine('const whereColumns = new Set(params?.where?.map(w => w.column) || []);');
|
135
|
+
writer.blankLine();
|
136
|
+
if (dynamicQueryInfo.with.length > 0) {
|
137
|
+
writer.writeLine(`const withFragments: string[] = [];`);
|
138
|
+
dynamicQueryInfo.with.forEach((withFragment) => {
|
139
|
+
var _a;
|
140
|
+
const selectConditions = withFragment.dependOnFields.map((fieldIndex) => `isSelected('${tsDescriptor.columns[fieldIndex].name}')`);
|
141
|
+
const whereConditions = withFragment.dependOnFields.map((fieldIndex) => `whereColumns.has('${tsDescriptor.columns[fieldIndex].name}')`);
|
142
|
+
const orderByConditions = ((_a = withFragment.dependOnOrderBy) === null || _a === void 0 ? void 0 : _a.map((orderBy) => `orderBy['${orderBy}'] != null`)) || [];
|
143
|
+
const allConditions = [...selectConditions, ...whereConditions, ...orderByConditions];
|
144
|
+
const paramValues = withFragment.parameters.map((paramIndex) => {
|
145
|
+
const param = tsDescriptor.parameters[paramIndex];
|
146
|
+
return `params?.params?.${param.name}`;
|
147
|
+
});
|
148
|
+
if (allConditions.length > 0) {
|
149
|
+
writer.writeLine(`if (`);
|
150
|
+
writer.indent().write(`${allConditions.join(`${node_os_1.EOL}\t|| `)}`).newLine();
|
151
|
+
writer.write(') ').inlineBlock(() => {
|
152
|
+
writer.write(`withFragments.push(\`${withFragment.fragment}\`);`);
|
153
|
+
paramValues.forEach((paramValues) => {
|
154
|
+
writer.writeLine(`paramsValues.push(${paramValues});`);
|
155
|
+
});
|
156
|
+
}).newLine();
|
157
|
+
}
|
158
|
+
else {
|
159
|
+
writer.writeLine(`withFragments.push(\`${withFragment.fragment}\`);`);
|
160
|
+
paramValues.forEach((paramValues) => {
|
161
|
+
writer.writeLine(`paramsValues.push(${paramValues});`);
|
162
|
+
});
|
163
|
+
}
|
164
|
+
});
|
165
|
+
}
|
166
|
+
dynamicQueryInfo.select.forEach((select, index) => {
|
167
|
+
writer.write(`if (isSelected('${tsDescriptor.columns[index].name}'))`).block(() => {
|
168
|
+
writer.writeLine(`selectedSqlFragments.push('${select.fragment}');`);
|
169
|
+
writer.writeLine(`selectedFields.push('${tsDescriptor.columns[index].name}');`);
|
170
|
+
select.parameters.forEach((param) => {
|
171
|
+
writer.writeLine(`paramsValues.push(params?.params?.${param} ?? null);`);
|
172
|
+
});
|
173
|
+
});
|
174
|
+
});
|
175
|
+
writer.blankLine();
|
176
|
+
writer.writeLine('const fromSqlFragments: string[] = [];');
|
177
|
+
dynamicQueryInfo.from.forEach((from) => {
|
178
|
+
var _a;
|
179
|
+
const selectConditions = from.dependOnFields.map((fieldIndex) => `isSelected('${tsDescriptor.columns[fieldIndex].name}')`);
|
180
|
+
const whereConditions = from.dependOnFields.map((fieldIndex) => `whereColumns.has('${tsDescriptor.columns[fieldIndex].name}')`);
|
181
|
+
const orderByConditions = ((_a = from.dependOnOrderBy) === null || _a === void 0 ? void 0 : _a.map((orderBy) => `orderBy['${orderBy}'] != null`)) || [];
|
182
|
+
const allConditions = [...selectConditions, ...whereConditions, ...orderByConditions];
|
183
|
+
const paramValues = from.parameters.map((paramIndex) => {
|
184
|
+
const param = tsDescriptor.parameters[paramIndex];
|
185
|
+
return `params?.params?.${param.name}`;
|
186
|
+
});
|
187
|
+
if (allConditions.length > 0) {
|
188
|
+
writer.blankLine();
|
189
|
+
writer.writeLine(`if (`);
|
190
|
+
writer.indent().write(`${allConditions.join(`${node_os_1.EOL}\t|| `)}`).newLine();
|
191
|
+
writer.write(') ').inlineBlock(() => {
|
192
|
+
writer.write(`fromSqlFragments.push(\`${from.fragment}\`);`);
|
193
|
+
});
|
194
|
+
paramValues.forEach((paramValues) => {
|
195
|
+
writer.writeLine(`paramsValues.push(${paramValues});`);
|
196
|
+
});
|
197
|
+
}
|
198
|
+
else {
|
199
|
+
writer.writeLine(`fromSqlFragments.push(\`${from.fragment}\`);`);
|
200
|
+
paramValues.forEach((paramValues) => {
|
201
|
+
writer.writeLine(`paramsValues.push(${paramValues});`);
|
202
|
+
});
|
203
|
+
}
|
204
|
+
});
|
205
|
+
writer.blankLine();
|
206
|
+
writer.writeLine('const whereSqlFragments: string[] = [];');
|
207
|
+
writer.blankLine();
|
208
|
+
dynamicQueryInfo.where.forEach((fragment) => {
|
209
|
+
const paramValues = fragment.parameters.map((paramIndex) => {
|
210
|
+
const param = tsDescriptor.parameters[paramIndex];
|
211
|
+
return `params?.params?.${param.name} ?? null`;
|
212
|
+
});
|
213
|
+
writer.writeLine(`whereSqlFragments.push(\`${fragment.fragment}\`);`);
|
214
|
+
paramValues.forEach((paramValues) => {
|
215
|
+
writer.writeLine(`paramsValues.push(${paramValues});`);
|
216
|
+
});
|
217
|
+
});
|
218
|
+
writer.writeLine(`let currentIndex = paramsValues.length;`);
|
219
|
+
writer.writeLine('const placeholder = () => `$${++currentIndex}`;');
|
220
|
+
writer.blankLine();
|
221
|
+
writer.write('params?.where?.forEach(condition => ').inlineBlock(() => {
|
222
|
+
writer.writeLine('const whereClause = whereCondition(condition, placeholder);');
|
223
|
+
dynamicQueryInfo.select.forEach((select, index) => {
|
224
|
+
if (select.parameters.length > 0) {
|
225
|
+
writer.write(`if (condition[0] == '${tsDescriptor.columns[index].name}')`).block(() => {
|
226
|
+
select.parameters.forEach((param) => {
|
227
|
+
writer.writeLine(`paramsValues.push(params?.params?.${param} ?? null);`);
|
228
|
+
});
|
229
|
+
});
|
230
|
+
}
|
231
|
+
});
|
232
|
+
writer.write('if (whereClause?.hasValue)').block(() => {
|
233
|
+
writer.writeLine(`whereSqlFragments.push(whereClause.sql);`);
|
234
|
+
writer.write('paramsValues.push(...whereClause.values);');
|
235
|
+
});
|
236
|
+
});
|
237
|
+
writer.write(');').newLine();
|
238
|
+
if (dynamicQueryInfo.with.length > 0) {
|
239
|
+
writer.blankLine();
|
240
|
+
writer.writeLine('const withSql = withFragments.length > 0');
|
241
|
+
writer.indent().write('? `WITH${EOL}${withFragments.join(`,${EOL}`)}${EOL}`').newLine();
|
242
|
+
writer.indent().write(`: '';`).newLine();
|
243
|
+
}
|
244
|
+
writer.blankLine();
|
245
|
+
writer.writeLine('const whereSql = whereSqlFragments.length > 0 ? `WHERE ${whereSqlFragments.join(\' AND \')}` : \'\';');
|
246
|
+
writer.blankLine();
|
247
|
+
if (dynamicQueryInfo.with.length > 0) {
|
248
|
+
writer.writeLine('const sql = `${withSql}SELECT');
|
249
|
+
}
|
250
|
+
else {
|
251
|
+
writer.writeLine('const sql = `SELECT');
|
252
|
+
}
|
253
|
+
writer.indent().write('${selectedSqlFragments.join(`,${EOL}`)}').newLine();
|
254
|
+
writer.indent().write('${fromSqlFragments.join(EOL)}').newLine();
|
255
|
+
;
|
256
|
+
writer.indent().write('${whereSql}`;').newLine();
|
257
|
+
writer.blankLine();
|
258
|
+
writer.writeLine('return { sql, paramsValues, selectedFields };');
|
259
|
+
});
|
260
|
+
writer.blankLine();
|
261
|
+
writer.write(`function mapArrayTo${resultTypeName}(data: any, selectedFields: (keyof ${resultTypeName})[])`).block(() => {
|
262
|
+
writer.writeLine(`const result: ${resultTypeName} = {};`);
|
263
|
+
writer.write(`selectedFields.forEach((field, index) => `).inlineBlock(() => {
|
264
|
+
writer.writeLine(`result[field] = data[index];`);
|
265
|
+
});
|
266
|
+
writer.write(');').newLine();
|
267
|
+
writer.write('return result;');
|
268
|
+
});
|
269
|
+
// if (orderByField != null) {
|
270
|
+
// writer.blankLine();
|
271
|
+
// writer.write(`function orderByToObject(orderBy: ${dynamicParamsTypeName}['orderBy'])`).block(() => {
|
272
|
+
// writer.writeLine('const obj = {} as any;');
|
273
|
+
// writer.write('orderBy?.forEach(order => ').inlineBlock(() => {
|
274
|
+
// writer.writeLine('obj[order[0]] = true;');
|
275
|
+
// });
|
276
|
+
// writer.write(');');
|
277
|
+
// writer.writeLine('return obj;');
|
278
|
+
// });
|
279
|
+
// }
|
280
|
+
writer.blankLine();
|
281
|
+
writer.write('type WhereConditionResult = ').block(() => {
|
282
|
+
writer.writeLine('sql: string;');
|
283
|
+
writer.writeLine('hasValue: boolean;');
|
284
|
+
writer.writeLine('values: any[];');
|
285
|
+
});
|
286
|
+
writer.blankLine();
|
287
|
+
(0, codegen_util_1.writeWhereConditionFunction)(writer, whereTypeName, tsDescriptor.columns);
|
288
|
+
}
|
289
|
+
if (tsDescriptor.nestedDescriptor2) {
|
290
|
+
const relations = tsDescriptor.nestedDescriptor2;
|
291
|
+
relations.forEach((relation) => {
|
292
|
+
const relationType = (0, sqlite_1.generateRelationType)(capitalizedName, relation.name);
|
293
|
+
writer.blankLine();
|
294
|
+
writer.write(`export type ${relationType} = `).block(() => {
|
295
|
+
const uniqueNameFields = (0, sqlite_1.renameInvalidNames)(relation.fields.map((f) => f.name));
|
296
|
+
relation.fields.forEach((field, index) => {
|
297
|
+
const nullable = field.notNull ? '' : ' | null';
|
298
|
+
writer.writeLine(`${uniqueNameFields[index]}: ${field.tsType}${nullable};`);
|
299
|
+
});
|
300
|
+
relation.relations.forEach((field) => {
|
301
|
+
const nestedRelationType = (0, sqlite_1.generateRelationType)(capitalizedName, field.tsType);
|
302
|
+
const nullable = field.notNull ? '' : ' | null';
|
303
|
+
writer.writeLine(`${field.name}: ${nestedRelationType}${nullable};`);
|
304
|
+
});
|
305
|
+
});
|
306
|
+
});
|
307
|
+
}
|
308
|
+
if (!dynamicQueryInfo) {
|
309
|
+
writer.blankLine();
|
310
|
+
const execFunctionParams = {
|
311
|
+
sql,
|
312
|
+
queryType: tsDescriptor.queryType,
|
313
|
+
multipleRowsResult: tsDescriptor.multipleRowsResult,
|
314
|
+
functionName: queryName,
|
315
|
+
paramsType: paramsTypeName,
|
316
|
+
dataType: dataTypeName,
|
317
|
+
returnType: resultTypeName,
|
318
|
+
columns: tsDescriptor.columns,
|
319
|
+
parameters: tsDescriptor.parameters,
|
320
|
+
data: tsDescriptor.data || [],
|
321
|
+
returning: schemaDef.returning || false,
|
322
|
+
orderByTypeName: orderByTypeName,
|
323
|
+
orderByColumns: tsDescriptor.orderByColumns || [],
|
324
|
+
generateNested: tsDescriptor.nestedDescriptor2 != null,
|
325
|
+
nestedType: tsDescriptor.nestedDescriptor2 ? tsDescriptor.nestedDescriptor2[0].name : '',
|
326
|
+
};
|
327
|
+
codeWriter.writeExecFunction(writer, execFunctionParams);
|
328
|
+
}
|
329
|
+
if (tsDescriptor.nestedDescriptor2) {
|
330
|
+
const relations = tsDescriptor.nestedDescriptor2;
|
331
|
+
relations.forEach((relation, index) => {
|
332
|
+
(0, code_generator_1.writeCollectFunction)(writer, relation, tsDescriptor.columns, capitalizedName, resultTypeName);
|
333
|
+
});
|
334
|
+
writer.blankLine();
|
335
|
+
writer.write('const groupBy = <T, Q>(array: T[], predicate: (value: T, index: number, array: T[]) => Q) =>').block(() => {
|
336
|
+
writer
|
337
|
+
.write('return array.reduce((map, value, index, array) => ')
|
338
|
+
.inlineBlock(() => {
|
339
|
+
writer.writeLine('const key = predicate(value, index, array);');
|
340
|
+
writer.writeLine('map.get(key)?.push(value) ?? map.set(key, [value]);');
|
341
|
+
writer.writeLine('return map;');
|
342
|
+
})
|
343
|
+
.write(', new Map<Q, T[]>());');
|
344
|
+
});
|
345
|
+
}
|
346
|
+
return writer.toString();
|
347
|
+
}
|
348
|
+
const isJsonType = (t) => {
|
349
|
+
return typeof t === 'object' && t !== null && 'name' in t;
|
350
|
+
};
|
351
|
+
const isJsonObjType = (t) => t.name === 'json';
|
352
|
+
const isJsonMapType = (t) => t.name === 'json_map';
|
353
|
+
const isJsonArrayType = (t) => t.name === 'json[]';
|
354
|
+
const isJsonFieldType = (t) => t.name === 'json_field';
|
355
|
+
function flattenJsonTypes(parentName, type) {
|
356
|
+
const result = [];
|
357
|
+
const visit = (typeName, t) => {
|
358
|
+
if (!isJsonType(t)) {
|
359
|
+
return;
|
360
|
+
}
|
361
|
+
if (isJsonObjType(t)) {
|
362
|
+
result.push({ typeName, type: t });
|
363
|
+
for (const prop of t.properties) {
|
364
|
+
visit(createJsonType(typeName, prop.key), prop.type);
|
365
|
+
}
|
366
|
+
}
|
367
|
+
else if (isJsonMapType(t)) {
|
368
|
+
visit(typeName, t.type);
|
369
|
+
}
|
370
|
+
else if (isJsonArrayType(t)) {
|
371
|
+
for (const itemType of t.properties) {
|
372
|
+
visit(typeName, itemType);
|
373
|
+
}
|
374
|
+
}
|
375
|
+
};
|
376
|
+
visit(parentName, type);
|
377
|
+
return result;
|
378
|
+
}
|
379
|
+
function writeDataType(writer, dataTypeName, params) {
|
380
|
+
writer.write(`export type ${dataTypeName} =`).block(() => {
|
381
|
+
params.forEach((field) => {
|
382
|
+
const optionalOp = field.optional ? '?' : '';
|
383
|
+
const orNull = field.notNull ? '' : ' | null';
|
384
|
+
writer.writeLine(`${field.name}${optionalOp}: ${field.tsType}${orNull};`);
|
385
|
+
});
|
386
|
+
});
|
387
|
+
}
|
388
|
+
function writeParamsType(writer, paramsTypeName, params, generateOrderBy, orderByTypeName) {
|
389
|
+
writer.write(`export type ${paramsTypeName} =`).block(() => {
|
390
|
+
params.forEach((field) => {
|
391
|
+
const optionalOp = field.optional ? '?' : '';
|
392
|
+
const orNull = field.notNull ? '' : ' | null';
|
393
|
+
writer.writeLine(`${field.name}${optionalOp}: ${field.tsType}${orNull};`);
|
394
|
+
});
|
395
|
+
if (generateOrderBy) {
|
396
|
+
writer.writeLine(`orderBy: ${orderByTypeName}[];`);
|
397
|
+
}
|
398
|
+
});
|
399
|
+
}
|
400
|
+
function writeResultType(writer, resultTypeName, columns) {
|
401
|
+
writer.write(`export type ${resultTypeName} =`).block(() => {
|
402
|
+
columns.forEach((field) => {
|
403
|
+
const optionalOp = field.optional ? '?' : '';
|
404
|
+
const nullable = field.notNull ? '' : ' | null';
|
405
|
+
writer.writeLine(`${field.name}${optionalOp}: ${field.tsType}${nullable};`);
|
406
|
+
});
|
407
|
+
});
|
408
|
+
}
|
409
|
+
function createJsonType(capitalizedName, columnName) {
|
410
|
+
const jsonType = (0, sqlite_1.capitalize)((0, sqlite_1.convertToCamelCaseName)(columnName));
|
411
|
+
const fullName = `${capitalizedName}${jsonType}`;
|
412
|
+
return fullName;
|
413
|
+
}
|
414
|
+
function writeJsonTypes(writer, typeName, type) {
|
415
|
+
writer.write(`export type ${typeName}Type =`).block(() => {
|
416
|
+
type.properties.forEach((field) => {
|
417
|
+
if (isJsonObjType(field.type)) {
|
418
|
+
const nullable = field.type.notNull ? '' : ' | null';
|
419
|
+
const jsonTypeName = createJsonType(typeName, field.key);
|
420
|
+
writer.writeLine(`${field.key}: ${jsonTypeName}Type${nullable};`);
|
421
|
+
}
|
422
|
+
else if (isJsonArrayType(field.type)) {
|
423
|
+
const jsonParentName = createJsonType(typeName, field.key);
|
424
|
+
const jsonTypeName = createJsonArrayType(jsonParentName, field.type);
|
425
|
+
writer.writeLine(`${field.key}: ${jsonTypeName};`);
|
426
|
+
}
|
427
|
+
else if (isJsonFieldType(field.type)) {
|
428
|
+
const nullable = field.type.notNull ? '' : ' | null';
|
429
|
+
writer.writeLine(`${field.key}: ${(0, postgres_1.mapColumnType)(field.type.type, true)}${nullable};`);
|
430
|
+
}
|
431
|
+
});
|
432
|
+
});
|
433
|
+
}
|
434
|
+
function createTsDescriptor(capitalizedName, schemaDef) {
|
435
|
+
var _a;
|
436
|
+
const tsDescriptor = {
|
437
|
+
columns: getColumnsForQuery(capitalizedName, schemaDef),
|
438
|
+
parameters: schemaDef.parameters.map((param) => mapParameterToTsFieldDescriptor(param)),
|
439
|
+
sql: '',
|
440
|
+
queryType: schemaDef.queryType,
|
441
|
+
multipleRowsResult: schemaDef.multipleRowsResult,
|
442
|
+
parameterNames: [],
|
443
|
+
data: (_a = schemaDef.data) === null || _a === void 0 ? void 0 : _a.map(param => mapParameterToTsFieldDescriptor(param))
|
444
|
+
};
|
445
|
+
if (schemaDef.orderByColumns) {
|
446
|
+
tsDescriptor.orderByColumns = schemaDef.orderByColumns;
|
447
|
+
}
|
448
|
+
if (schemaDef.nestedInfo) {
|
449
|
+
const nestedDescriptor2 = schemaDef.nestedInfo.map((relation) => {
|
450
|
+
const tsRelation = {
|
451
|
+
groupIndex: relation.groupIndex,
|
452
|
+
name: relation.name,
|
453
|
+
fields: relation.fields.map((field) => (0, code_generator_1.mapPostgrsFieldToTsField)(schemaDef.columns, field)),
|
454
|
+
relations: relation.relations.map((relation) => (0, ts_nested_descriptor_1.mapToTsRelation2)(relation))
|
455
|
+
};
|
456
|
+
return tsRelation;
|
457
|
+
});
|
458
|
+
tsDescriptor.nestedDescriptor2 = nestedDescriptor2;
|
459
|
+
}
|
460
|
+
if (schemaDef.dynamicSqlQuery2) {
|
461
|
+
tsDescriptor.dynamicQuery2 = schemaDef.dynamicSqlQuery2;
|
462
|
+
}
|
463
|
+
return tsDescriptor;
|
464
|
+
}
|
465
|
+
function createJsonArrayType(name, type) {
|
466
|
+
const typeNames = type.properties.flatMap(p => {
|
467
|
+
if (isJsonFieldType(p)) {
|
468
|
+
const baseType = (0, postgres_1.mapColumnType)(p.type, true);
|
469
|
+
if (!p.notNull) {
|
470
|
+
return [baseType, 'null'];
|
471
|
+
}
|
472
|
+
return [baseType];
|
473
|
+
}
|
474
|
+
return createTsType(name, p);
|
475
|
+
});
|
476
|
+
const uniqTypeNames = [...new Set(typeNames)];
|
477
|
+
const unionTypes = uniqTypeNames.join(' | ');
|
478
|
+
return uniqTypeNames.length === 1 ? `${unionTypes}[]` : `(${unionTypes})[]`;
|
479
|
+
}
|
480
|
+
function createJsonMapType(name, type) {
|
481
|
+
const valueType = isJsonFieldType(type.type) ? (0, postgres_1.mapColumnType)(type.type.type, true) : `${name}Type`;
|
482
|
+
return `Record<string, ${valueType} | undefined>`;
|
483
|
+
}
|
484
|
+
function createTsType(name, type) {
|
485
|
+
if (isJsonType(type)) {
|
486
|
+
if (isJsonObjType(type)) {
|
487
|
+
return `${name}Type`;
|
488
|
+
}
|
489
|
+
else if (isJsonArrayType(type)) {
|
490
|
+
return createJsonArrayType(name, type);
|
491
|
+
}
|
492
|
+
else if (isJsonMapType(type)) {
|
493
|
+
return createJsonMapType(name, type);
|
494
|
+
}
|
495
|
+
}
|
496
|
+
return (0, postgres_1.mapColumnType)(type);
|
497
|
+
}
|
498
|
+
function mapColumnInfoToTsFieldDescriptor(capitalizedName, col, dynamicQuery) {
|
499
|
+
const typeName = createJsonType(capitalizedName, col.name);
|
500
|
+
const tsField = {
|
501
|
+
name: col.name,
|
502
|
+
tsType: createTsType(typeName, col.type),
|
503
|
+
optional: dynamicQuery ? true : false,
|
504
|
+
notNull: dynamicQuery ? true : col.notNull
|
505
|
+
};
|
506
|
+
return tsField;
|
507
|
+
}
|
508
|
+
function mapParameterToTsFieldDescriptor(param) {
|
509
|
+
const tsDesc = {
|
510
|
+
name: param.name,
|
511
|
+
tsType: (0, postgres_1.mapColumnType)(param.type),
|
512
|
+
notNull: param.notNull ? param.notNull : false,
|
513
|
+
toDriver: '',
|
514
|
+
isArray: param.type.startsWith('_')
|
515
|
+
};
|
516
|
+
return tsDesc;
|
517
|
+
}
|
518
|
+
function getCodeWriter(client) {
|
519
|
+
return postgresCodeWriter;
|
520
|
+
}
|
521
|
+
const postgresCodeWriter = {
|
522
|
+
writeImports: function (writer, queryType) {
|
523
|
+
writer.writeLine(`import pg from 'pg';`);
|
524
|
+
if (queryType === 'Copy') {
|
525
|
+
writer.writeLine(`import { from as copyFrom } from 'pg-copy-streams';`);
|
526
|
+
writer.writeLine(`import { pipeline } from 'stream/promises';`);
|
527
|
+
writer.writeLine(`import { Readable } from 'stream';`);
|
528
|
+
}
|
529
|
+
},
|
530
|
+
writeExecFunction: function (writer, params) {
|
531
|
+
if (params.queryType === 'Copy') {
|
532
|
+
_writeCopyFunction(writer, params);
|
533
|
+
}
|
534
|
+
else {
|
535
|
+
_writeExecFunction(writer, params);
|
536
|
+
}
|
537
|
+
}
|
538
|
+
};
|
539
|
+
function _writeCopyFunction(writer, params) {
|
540
|
+
const { functionName, paramsType } = params;
|
541
|
+
let functionParams = `client: pg.Client | pg.PoolClient, values: ${paramsType}[]`;
|
542
|
+
writer.write(`export async function ${functionName}(${functionParams}): Promise<void>`).block(() => {
|
543
|
+
writeSql(writer, params.sql);
|
544
|
+
writer.writeLine('const csv = jsonToCsv(values);');
|
545
|
+
writer.blankLine();
|
546
|
+
writer.writeLine('const sourceStream = Readable.from(csv);');
|
547
|
+
writer.writeLine('const stream = client.query(copyFrom(sql));');
|
548
|
+
writer.writeLine('await pipeline(sourceStream, stream)');
|
549
|
+
});
|
550
|
+
writer.blankLine();
|
551
|
+
writer.write(`function jsonToCsv(values: ${paramsType}[]): string`).block(() => {
|
552
|
+
writer.writeLine('return values');
|
553
|
+
writer.indent().write('.map(value =>').newLine();
|
554
|
+
writer.indent(2).write('Object.values(value)').newLine();
|
555
|
+
writer.indent(3).write('.map(val => escapeValue(val))').newLine();
|
556
|
+
writer.indent(3).write(`.join(',')`).newLine();
|
557
|
+
writer.indent().write(')').newLine();
|
558
|
+
writer.indent().write(`.join('\\n');`).newLine();
|
559
|
+
});
|
560
|
+
writer.blankLine();
|
561
|
+
writer.write(`function escapeValue(val: any): string`).block(() => {
|
562
|
+
writer.writeLine(`return val != null ? JSON.stringify(val).replace(/\\n/g, '\\\\n') : '';`);
|
563
|
+
});
|
564
|
+
}
|
565
|
+
function _writeExecFunction(writer, params) {
|
566
|
+
const { functionName, paramsType, dataType, returnType, parameters, orderByTypeName, orderByColumns, generateNested, nestedType } = params;
|
567
|
+
let functionParams = params.queryType === 'Copy' ? 'client: pg.Client | pg.PoolClient' : 'client: pg.Client | pg.Pool | pg.PoolClient';
|
568
|
+
if (params.data.length > 0) {
|
569
|
+
functionParams += `, data: ${dataType}`;
|
570
|
+
}
|
571
|
+
if (parameters.length > 0 || orderByColumns.length > 0) {
|
572
|
+
functionParams += `, params: ${paramsType}`;
|
573
|
+
}
|
574
|
+
const allParamters = [...params.data.map(param => paramToDriver(param, 'data')),
|
575
|
+
...parameters.map(param => paramToDriver(param, 'params')),
|
576
|
+
...parameters.filter(param => isList(param)).map(param => `...params.${param.name}.slice(1)`)];
|
577
|
+
const paramValues = allParamters.length > 0 ? `, values: [${allParamters.join(', ')}]` : '';
|
578
|
+
const orNull = params.queryType === 'Select' ? ' | null' : '';
|
579
|
+
const functionReturnType = params.multipleRowsResult ? `${returnType}[]` : `${returnType}${orNull}`;
|
580
|
+
const hasListParams = parameters.some(param => !param.isArray && param.tsType.endsWith('[]'));
|
581
|
+
if (hasListParams) {
|
582
|
+
writer.writeLine('let currentIndex: number;');
|
583
|
+
}
|
584
|
+
writer.write(`export async function ${functionName}(${functionParams}): Promise<${functionReturnType}>`).block(() => {
|
585
|
+
if (hasListParams) {
|
586
|
+
writer.writeLine(`currentIndex = ${params.data.length + params.parameters.length};`);
|
587
|
+
}
|
588
|
+
writeSql(writer, params.sql);
|
589
|
+
if (params.queryType === 'Select' || params.returning) {
|
590
|
+
writer.write(`return client.query({ text: sql, rowMode: 'array'${paramValues} })`).newLine();
|
591
|
+
if (params.multipleRowsResult) {
|
592
|
+
writer.indent().write(`.then(res => res.rows.map(row => mapArrayTo${returnType}(row)));`);
|
593
|
+
}
|
594
|
+
else if (params.returning) {
|
595
|
+
writer.indent().write(`.then(res => mapArrayTo${returnType}(res.rows[0]));`);
|
596
|
+
}
|
597
|
+
else {
|
598
|
+
writer.indent().write(`.then(res => res.rows.length > 0 ? mapArrayTo${returnType}(res.rows[0]) : null);`);
|
599
|
+
}
|
600
|
+
}
|
601
|
+
else {
|
602
|
+
writer.write(`return client.query({ text: sql${paramValues} })`).newLine();
|
603
|
+
writer.indent().write(`.then(res => mapArrayTo${returnType}(res));`);
|
604
|
+
}
|
605
|
+
});
|
606
|
+
if (hasListParams) {
|
607
|
+
writer.blankLine();
|
608
|
+
writer.write(`function generatePlaceholders(param: string, paramsArray: any[]): string`).block(() => {
|
609
|
+
writer.write('return paramsArray').newLine();
|
610
|
+
writer.indent().write('.map((_, index) => {').newLine();
|
611
|
+
writer.indent(2).write(`if (index === 0) {`).newLine();
|
612
|
+
writer.indent(3).write(`return param`).newLine();
|
613
|
+
writer.indent(2).write(`}`).newLine();
|
614
|
+
writer.indent(2).write('currentIndex++;').newLine();
|
615
|
+
writer.indent(2).write('return `$${currentIndex}`;').newLine();
|
616
|
+
writer.indent().write('})');
|
617
|
+
writer.newLine();
|
618
|
+
writer.indent().write(`.join(', ');`);
|
619
|
+
});
|
620
|
+
}
|
621
|
+
writer.blankLine();
|
622
|
+
writer.write(`function mapArrayTo${returnType}(data: any) `).block(() => {
|
623
|
+
writer.write(`const result: ${returnType} = `).block(() => {
|
624
|
+
params.columns.forEach((col, index) => {
|
625
|
+
const separator = index < params.columns.length - 1 ? ',' : '';
|
626
|
+
if (params.queryType === 'Select' || params.returning) {
|
627
|
+
writer.writeLine(`${col.name}: ${toDriver(`data[${index}]`, col)}${separator}`);
|
628
|
+
}
|
629
|
+
else {
|
630
|
+
writer.writeLine(`${col.name}: data.${col.name}${separator}`);
|
631
|
+
}
|
632
|
+
});
|
633
|
+
});
|
634
|
+
writer.writeLine('return result;');
|
635
|
+
});
|
636
|
+
if (orderByColumns.length > 0) {
|
637
|
+
writer.blankLine();
|
638
|
+
writer.writeLine(`const orderByColumns = [${orderByColumns.map(col => `'${col}'`).join(', ')}] as const;`);
|
639
|
+
writer.blankLine();
|
640
|
+
writer.write(`export type ${orderByTypeName} =`).block(() => {
|
641
|
+
writer.writeLine('column: typeof orderByColumns[number];');
|
642
|
+
writer.writeLine(`direction: 'asc' | 'desc';`);
|
643
|
+
});
|
644
|
+
writer.blankLine();
|
645
|
+
writer.write(`function buildOrderBy(orderBy: ${orderByTypeName}[]): string`).block(() => {
|
646
|
+
writer.write('if (!Array.isArray(orderBy) || orderBy.length === 0)').block(() => {
|
647
|
+
writer.writeLine(`throw new Error('orderBy must be a non-empty array');`);
|
648
|
+
});
|
649
|
+
writer.blankLine();
|
650
|
+
writer.write('for (const { column, direction } of orderBy)').block(() => {
|
651
|
+
writer.write('if (!orderByColumns.includes(column))').block(() => {
|
652
|
+
writer.writeLine('throw new Error(`Invalid orderBy column: ${column}`);');
|
653
|
+
});
|
654
|
+
writer.write(`if (direction !== 'asc' && direction !== 'desc')`).block(() => {
|
655
|
+
writer.writeLine('throw new Error(`Invalid orderBy direction: ${direction}`);');
|
656
|
+
});
|
657
|
+
});
|
658
|
+
writer.blankLine();
|
659
|
+
writer.writeLine('return orderBy');
|
660
|
+
writer.indent().write('.map(({ column, direction }) => `"${column}" ${direction.toUpperCase()}`)').newLine();
|
661
|
+
writer.indent().write(`.join(', ');`).newLine();
|
662
|
+
});
|
663
|
+
}
|
664
|
+
if (generateNested) {
|
665
|
+
writer.blankLine();
|
666
|
+
const relationType = (0, sqlite_1.generateRelationType)(functionName, nestedType);
|
667
|
+
writer.write(`export async function ${functionName}Nested(${functionParams}): Promise<${relationType}[]>`).block(() => {
|
668
|
+
const params = parameters.length > 0 ? ', params' : '';
|
669
|
+
writer.writeLine(`const selectResult = await ${functionName}(client${params});`);
|
670
|
+
writer.write('if (selectResult.length == 0)').block(() => {
|
671
|
+
writer.writeLine('return [];');
|
672
|
+
});
|
673
|
+
writer.writeLine(`return collect${relationType}(selectResult);`);
|
674
|
+
});
|
675
|
+
}
|
676
|
+
}
|
677
|
+
function writeSql(writer, sql) {
|
678
|
+
const sqlSplit = sql.trimEnd().split('\n');
|
679
|
+
writer.write('const sql = `').newLine();
|
680
|
+
sqlSplit.forEach((sqlLine) => {
|
681
|
+
writer.indent().write(sqlLine.trimEnd()).newLine();
|
682
|
+
});
|
683
|
+
writer.indent().write('`').newLine();
|
684
|
+
}
|
685
|
+
function getFunctionReturnType(queryType, multipleRowsResult, returnType) {
|
686
|
+
if (queryType === 'Copy') {
|
687
|
+
return 'void';
|
688
|
+
}
|
689
|
+
if (multipleRowsResult) {
|
690
|
+
return `${returnType}[]`;
|
691
|
+
}
|
692
|
+
const orNull = queryType === 'Select' ? ' | null' : '';
|
693
|
+
return `${returnType}${orNull}`;
|
694
|
+
}
|
695
|
+
function getColumnsForQuery(capitalizedName, schemaDef) {
|
696
|
+
if (schemaDef.queryType === 'Select' || schemaDef.returning) {
|
697
|
+
const columns = schemaDef.columns.map(col => mapColumnInfoToTsFieldDescriptor(capitalizedName, col, schemaDef.dynamicSqlQuery2 != null));
|
698
|
+
const escapedColumnsNames = (0, sqlite_1.renameInvalidNames)(schemaDef.columns.map((col) => col.name));
|
699
|
+
return columns.map((col, index) => (Object.assign(Object.assign({}, col), { name: escapedColumnsNames[index] })));
|
700
|
+
}
|
701
|
+
if (schemaDef.queryType === 'Copy') {
|
702
|
+
return [];
|
703
|
+
}
|
704
|
+
const columns = [
|
705
|
+
{
|
706
|
+
name: 'rowCount',
|
707
|
+
tsType: 'number',
|
708
|
+
notNull: true
|
709
|
+
}
|
710
|
+
];
|
711
|
+
return columns;
|
712
|
+
}
|
713
|
+
function toDriver(variableData, param) {
|
714
|
+
if (param.tsType === 'Date') {
|
715
|
+
if (param.notNull && !param.optional) {
|
716
|
+
return `new Date(${variableData})`;
|
717
|
+
}
|
718
|
+
return `${variableData} != null ? new Date(${variableData}) : ${variableData}`;
|
719
|
+
}
|
720
|
+
if (param.tsType === 'boolean') {
|
721
|
+
return `${variableData} != null ? Boolean(${variableData}) : ${variableData}`;
|
722
|
+
}
|
723
|
+
return variableData;
|
724
|
+
}
|
725
|
+
function paramToDriver(param, objName) {
|
726
|
+
if (!param.tsType.endsWith('[]')) {
|
727
|
+
return `${objName}.${param.name}`;
|
728
|
+
}
|
729
|
+
return param.isArray ? `[...${objName}.${param.name}]` : `${objName}.${param.name}[0]`;
|
730
|
+
}
|
731
|
+
function isList(param) {
|
732
|
+
return param.tsType.endsWith('[]') && !param.isArray;
|
733
|
+
}
|
734
|
+
function generateCrud(queryType, tableName, dbSchema) {
|
735
|
+
const queryName = (0, code_generator_1.getQueryName)(queryType, tableName);
|
736
|
+
const camelCaseName = (0, sqlite_1.convertToCamelCaseName)(queryName);
|
737
|
+
const capitalizedName = (0, sqlite_1.capitalize)(camelCaseName);
|
738
|
+
const dataTypeName = `${capitalizedName}Data`;
|
739
|
+
const resultTypeName = `${capitalizedName}Result`;
|
740
|
+
const paramsTypeName = `${capitalizedName}Params`;
|
741
|
+
const writer = createCodeBlockWriter();
|
742
|
+
const allColumns = dbSchema.filter((col) => col.table === tableName);
|
743
|
+
const keyColumns = allColumns.filter((col) => col.column_key === 'PRI');
|
744
|
+
if (keyColumns.length === 0) {
|
745
|
+
keyColumns.push(...allColumns.filter((col) => col.column_key === 'UNI'));
|
746
|
+
}
|
747
|
+
const keys = keyColumns.map(col => (Object.assign(Object.assign({}, mapPostgresColumnSchemaToTsFieldDescriptor(col)), { optional: false })));
|
748
|
+
const nonKeys = allColumns.filter(col => col.column_key !== 'PRI').map(col => mapPostgresColumnSchemaToTsFieldDescriptor(col));
|
749
|
+
const codeWriter = getCodeWriter('pg');
|
750
|
+
codeWriter.writeImports(writer, queryType);
|
751
|
+
const uniqueDataParams = queryType === 'Update' ? nonKeys.map(col => (Object.assign(Object.assign({}, col), { optional: true }))) : [];
|
752
|
+
if (uniqueDataParams.length > 0) {
|
753
|
+
writer.blankLine();
|
754
|
+
writeDataType(writer, dataTypeName, uniqueDataParams);
|
755
|
+
}
|
756
|
+
const uniqueParams = queryType === 'Insert' ? nonKeys : keys;
|
757
|
+
if (uniqueParams.length > 0) {
|
758
|
+
writer.blankLine();
|
759
|
+
writeParamsType(writer, paramsTypeName, uniqueParams, false, '');
|
760
|
+
}
|
761
|
+
writer.blankLine();
|
762
|
+
const columns = allColumns.map(col => (Object.assign(Object.assign({}, mapPostgresColumnSchemaToTsFieldDescriptor(col)), { optional: false })));
|
763
|
+
writeResultType(writer, resultTypeName, columns);
|
764
|
+
writer.blankLine();
|
765
|
+
const crudParameters = {
|
766
|
+
queryType,
|
767
|
+
tableName,
|
768
|
+
queryName,
|
769
|
+
dataTypeName,
|
770
|
+
paramsTypeName,
|
771
|
+
resultTypeName,
|
772
|
+
columns,
|
773
|
+
nonKeys: nonKeys,
|
774
|
+
keys: keys.map(col => col.name)
|
775
|
+
};
|
776
|
+
const result = writeCrud(writer, crudParameters);
|
777
|
+
return result;
|
778
|
+
}
|
779
|
+
function writeCrud(writer, crudParamters) {
|
780
|
+
const { queryType } = crudParamters;
|
781
|
+
switch (queryType) {
|
782
|
+
case 'Select':
|
783
|
+
return writeCrudSelect(writer, crudParamters);
|
784
|
+
case 'Insert':
|
785
|
+
return writeCrudInsert(writer, crudParamters);
|
786
|
+
case 'Update':
|
787
|
+
return writeCrudUpdate(writer, crudParamters);
|
788
|
+
case 'Delete':
|
789
|
+
return writeCrudDelete(writer, crudParamters);
|
790
|
+
}
|
791
|
+
}
|
792
|
+
function writeCrudSelect(writer, crudParamters) {
|
793
|
+
const { tableName, queryName, paramsTypeName, resultTypeName, columns, keys } = crudParamters;
|
794
|
+
writer.write(`export async function ${queryName}(client: pg.Client | pg.Pool | pg.PoolClient, params: ${paramsTypeName}): Promise<${resultTypeName} | null>`).block(() => {
|
795
|
+
writer.writeLine('const sql = `');
|
796
|
+
writer.indent().write('SELECT').newLine();
|
797
|
+
columns.forEach((col, columnIndex) => {
|
798
|
+
writer.indent(2).write(col.name);
|
799
|
+
writer.conditionalWrite(columnIndex < columns.length - 1, ',');
|
800
|
+
writer.newLine();
|
801
|
+
});
|
802
|
+
writer.indent().write(`FROM ${tableName}`).newLine();
|
803
|
+
const keyName = keys[0];
|
804
|
+
writer.indent().write(`WHERE ${keyName} = $1`).newLine();
|
805
|
+
writer.indent().write('`').newLine();
|
806
|
+
writer.writeLine(`return client.query({ text: sql, rowMode: 'array', values: [params.${keyName}] })`);
|
807
|
+
writer.indent(1).write(`.then(res => res.rows.length > 0 ? mapArrayTo${resultTypeName}(res.rows[0]) : null);`).newLine();
|
808
|
+
});
|
809
|
+
writer.blankLine();
|
810
|
+
writer.write(`function mapArrayTo${resultTypeName}(data: any) `).block(() => {
|
811
|
+
writer.write(`const result: ${resultTypeName} = `).block(() => {
|
812
|
+
columns.forEach((col, index) => {
|
813
|
+
const separator = index < columns.length - 1 ? ',' : '';
|
814
|
+
writer.writeLine(`${col.name}: ${toDriver(`data[${index}]`, col)}${separator}`);
|
815
|
+
});
|
816
|
+
});
|
817
|
+
writer.writeLine('return result;');
|
818
|
+
});
|
819
|
+
return writer.toString();
|
820
|
+
}
|
821
|
+
function writeCrudInsert(writer, crudParamters) {
|
822
|
+
const { tableName, queryName, dataTypeName, paramsTypeName, resultTypeName, columns, nonKeys, keys } = crudParamters;
|
823
|
+
writer.write(`export async function ${queryName}(client: pg.Client | pg.Pool | pg.PoolClient, params: ${paramsTypeName}): Promise<${resultTypeName} | null>`).block(() => {
|
824
|
+
const hasOptional = nonKeys.some(field => field.optional);
|
825
|
+
if (hasOptional) {
|
826
|
+
writer.writeLine(`const insertColumns = [${nonKeys.map(col => `'${col.name}'`).join(', ')}] as const;`);
|
827
|
+
writer.writeLine('const columns: string[] = [];');
|
828
|
+
writer.writeLine('const placeholders: string[] = [];');
|
829
|
+
writer.writeLine('const values: unknown[] = [];');
|
830
|
+
writer.blankLine();
|
831
|
+
writer.writeLine('let parameterNumber = 1;');
|
832
|
+
writer.blankLine();
|
833
|
+
writer.write('for (const column of insertColumns)').block(() => {
|
834
|
+
writer.writeLine('const value = params[column];');
|
835
|
+
writer.write('if (value !== undefined)').block(() => {
|
836
|
+
writer.writeLine('columns.push(column);');
|
837
|
+
writer.writeLine('placeholders.push(`$${parameterNumber++}`);');
|
838
|
+
writer.writeLine('values.push(value);');
|
839
|
+
});
|
840
|
+
});
|
841
|
+
writer.blankLine();
|
842
|
+
writer.writeLine('const sql = columns.length === 0');
|
843
|
+
writer.indent().write('? `INSERT INTO roles DEFAULT VALUES RETURNING *`').newLine();
|
844
|
+
writer.indent().write(': `INSERT INTO roles (${columns.join(\', \')})').newLine();
|
845
|
+
writer.indent().write(`VALUES(\${placeholders.join(', ')})`).newLine();
|
846
|
+
writer.indent().write('RETURNING *`').newLine();
|
847
|
+
writer.blankLine();
|
848
|
+
writer.writeLine(`return client.query({ text: sql, values })`);
|
849
|
+
}
|
850
|
+
else {
|
851
|
+
writer.writeLine('const sql = `');
|
852
|
+
writer.indent().write(`INSERT INTO ${tableName} (${nonKeys.map(field => field.name).join(',')})`).newLine();
|
853
|
+
writer.indent().write(`VALUES (${nonKeys.map((_, index) => `$${index + 1}`).join(',')})`).newLine();
|
854
|
+
writer.indent().write('RETURNING *').newLine();
|
855
|
+
writer.indent().write('`').newLine();
|
856
|
+
writer.writeLine(`return client.query({ text: sql, values: [${nonKeys.map(col => `params.${col.name}`)}] })`);
|
857
|
+
}
|
858
|
+
writer.indent().write(`.then(res => res.rows[0] ?? null);`);
|
859
|
+
});
|
860
|
+
return writer.toString();
|
861
|
+
}
|
862
|
+
function writeCrudUpdate(writer, crudParamters) {
|
863
|
+
const { tableName, queryName, dataTypeName, paramsTypeName, resultTypeName, columns, nonKeys, keys } = crudParamters;
|
864
|
+
writer.write(`export async function ${queryName}(client: pg.Client | pg.Pool | pg.PoolClient, data: ${dataTypeName}, params: ${paramsTypeName}): Promise<${resultTypeName} | null>`).block(() => {
|
865
|
+
writer.writeLine(`const updateColumns = [${nonKeys.map(col => `'${col.name}'`).join(', ')}] as const;`);
|
866
|
+
writer.writeLine('const updates: string[] = [];');
|
867
|
+
writer.writeLine('const values: unknown[] = [];');
|
868
|
+
writer.writeLine('let parameterNumber = 1;');
|
869
|
+
writer.blankLine();
|
870
|
+
writer.write('for (const column of updateColumns)').block(() => {
|
871
|
+
writer.writeLine('const value = data[column];');
|
872
|
+
writer.write('if (value !== undefined)').block(() => {
|
873
|
+
writer.writeLine('updates.push(`${column} = $${parameterNumber++}`);');
|
874
|
+
writer.writeLine('values.push(value);');
|
875
|
+
});
|
876
|
+
});
|
877
|
+
writer.writeLine('if (updates.length === 0) return null;');
|
878
|
+
const keyName = keys[0];
|
879
|
+
writer.writeLine(`values.push(params.${keyName});`);
|
880
|
+
writer.blankLine();
|
881
|
+
writer.writeLine(`const sql = \`UPDATE ${tableName} SET \${updates.join(', ')} WHERE ${keyName} = \$\${parameterNumber} RETURNING *\`;`);
|
882
|
+
writer.writeLine('return client.query({ text: sql, values })');
|
883
|
+
writer.indent().write('.then(res => res.rows[0] ?? null);');
|
884
|
+
});
|
885
|
+
return writer.toString();
|
886
|
+
}
|
887
|
+
function writeCrudDelete(writer, crudParamters) {
|
888
|
+
const { tableName, queryName, paramsTypeName, resultTypeName, columns, keys } = crudParamters;
|
889
|
+
const keyName = keys[0];
|
890
|
+
writer.write(`export async function ${queryName}(client: pg.Client | pg.Pool | pg.PoolClient, params: ${paramsTypeName}): Promise<${resultTypeName} | null>`).block(() => {
|
891
|
+
writer.writeLine('const sql = `');
|
892
|
+
writer.indent().write(`DELETE FROM ${tableName} WHERE ${keyName} = $1`).newLine();
|
893
|
+
writer.indent().write('`').newLine();
|
894
|
+
writer.writeLine(`return client.query({ text: sql, rowMode: 'array', values: [params.${keyName}] })`);
|
895
|
+
writer.indent(1).write(`.then(res => res.rows.length > 0 ? mapArrayTo${resultTypeName}(res.rows[0]) : null);`).newLine();
|
896
|
+
});
|
897
|
+
writer.blankLine();
|
898
|
+
writer.write(`function mapArrayTo${resultTypeName}(data: any) `).block(() => {
|
899
|
+
writer.write(`const result: ${resultTypeName} = `).block(() => {
|
900
|
+
columns.forEach((col, index) => {
|
901
|
+
const separator = index < columns.length - 1 ? ',' : '';
|
902
|
+
writer.writeLine(`${col.name}: ${toDriver(`data[${index}]`, col)}${separator}`);
|
903
|
+
});
|
904
|
+
});
|
905
|
+
writer.writeLine('return result;');
|
906
|
+
});
|
907
|
+
return writer.toString();
|
908
|
+
}
|
909
|
+
function mapPostgresColumnSchemaToTsFieldDescriptor(col) {
|
910
|
+
return {
|
911
|
+
name: col.column_name,
|
912
|
+
notNull: !col.is_nullable,
|
913
|
+
optional: col.column_default,
|
914
|
+
tsType: (0, postgres_1.mapColumnType)(col.type),
|
915
|
+
};
|
916
|
+
}
|
917
|
+
//# sourceMappingURL=postgres-pg.js.map
|