@sqg/sqg 0.8.0 → 0.10.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/dist/sqg.mjs +107 -50
- package/dist/templates/java-jdbc.hbs +38 -2
- package/dist/templates/turso.hbs +14 -18
- package/dist/templates/typescript-duckdb.hbs +26 -3
- package/package.json +1 -1
package/dist/sqg.mjs
CHANGED
|
@@ -680,6 +680,8 @@ var EnumType = class {
|
|
|
680
680
|
var SQLQuery = class {
|
|
681
681
|
columns;
|
|
682
682
|
allColumns;
|
|
683
|
+
/** Database-reported parameter types (variable name → SQL type), set by database adapters */
|
|
684
|
+
parameterTypes;
|
|
683
685
|
constructor(filename, id, rawQuery, queryAnonymous, queryNamed, queryPositional, type, isOne, isPluck, variables, config) {
|
|
684
686
|
this.filename = filename;
|
|
685
687
|
this.id = id;
|
|
@@ -964,6 +966,25 @@ async function initializeDatabase(queries, execQueries) {
|
|
|
964
966
|
|
|
965
967
|
//#endregion
|
|
966
968
|
//#region src/db/duckdb.ts
|
|
969
|
+
function convertType(type) {
|
|
970
|
+
if (type instanceof DuckDBListType) return new ListType(convertType(type.valueType));
|
|
971
|
+
if (type instanceof DuckDBStructType) return new StructType(type.entryTypes.map((t, index) => ({
|
|
972
|
+
name: type.entryNames[index],
|
|
973
|
+
type: convertType(t),
|
|
974
|
+
nullable: true
|
|
975
|
+
})));
|
|
976
|
+
if (type instanceof DuckDBMapType) return new MapType({
|
|
977
|
+
name: "key",
|
|
978
|
+
type: convertType(type.keyType),
|
|
979
|
+
nullable: true
|
|
980
|
+
}, {
|
|
981
|
+
name: "value",
|
|
982
|
+
type: convertType(type.valueType),
|
|
983
|
+
nullable: true
|
|
984
|
+
});
|
|
985
|
+
if (type instanceof DuckDBEnumType) return new EnumType(type.values);
|
|
986
|
+
return type.toString();
|
|
987
|
+
}
|
|
967
988
|
const duckdb = new class {
|
|
968
989
|
db;
|
|
969
990
|
connection;
|
|
@@ -1003,6 +1024,15 @@ const duckdb = new class {
|
|
|
1003
1024
|
return ` ${part.value} `;
|
|
1004
1025
|
}).join("");
|
|
1005
1026
|
const stmt = await connection.prepare(sql);
|
|
1027
|
+
if (stmt.parameterCount > 0) {
|
|
1028
|
+
const paramTypes = /* @__PURE__ */ new Map();
|
|
1029
|
+
for (let i = 0; i < stmt.parameterCount; i++) {
|
|
1030
|
+
const paramType = stmt.parameterType(i + 1);
|
|
1031
|
+
paramTypes.set(statement.parameters[i].name, convertType(paramType));
|
|
1032
|
+
}
|
|
1033
|
+
query.parameterTypes = paramTypes;
|
|
1034
|
+
consola.debug("Parameter types:", Object.fromEntries(paramTypes));
|
|
1035
|
+
}
|
|
1006
1036
|
for (let i = 0; i < stmt.parameterCount; i++) stmt.bindValue(i + 1, statement.parameters[i].value);
|
|
1007
1037
|
if (query.isQuery) {
|
|
1008
1038
|
const result = await stmt.stream();
|
|
@@ -1010,25 +1040,6 @@ const duckdb = new class {
|
|
|
1010
1040
|
const columnTypes = result.columnTypes();
|
|
1011
1041
|
consola.debug("Columns:", columnNames);
|
|
1012
1042
|
consola.debug("Types:", columnTypes.map((t) => `${t.toString()} / ${t.constructor.name}`));
|
|
1013
|
-
function convertType(type) {
|
|
1014
|
-
if (type instanceof DuckDBListType) return new ListType(convertType(type.valueType));
|
|
1015
|
-
if (type instanceof DuckDBStructType) return new StructType(type.entryTypes.map((t, index) => ({
|
|
1016
|
-
name: type.entryNames[index],
|
|
1017
|
-
type: convertType(t),
|
|
1018
|
-
nullable: true
|
|
1019
|
-
})));
|
|
1020
|
-
if (type instanceof DuckDBMapType) return new MapType({
|
|
1021
|
-
name: "key",
|
|
1022
|
-
type: convertType(type.keyType),
|
|
1023
|
-
nullable: true
|
|
1024
|
-
}, {
|
|
1025
|
-
name: "value",
|
|
1026
|
-
type: convertType(type.valueType),
|
|
1027
|
-
nullable: true
|
|
1028
|
-
});
|
|
1029
|
-
if (type instanceof DuckDBEnumType) return new EnumType(type.values);
|
|
1030
|
-
return type.toString();
|
|
1031
|
-
}
|
|
1032
1043
|
query.columns = columnNames.map((name, index) => ({
|
|
1033
1044
|
name,
|
|
1034
1045
|
type: convertType(columnTypes[index]),
|
|
@@ -1051,33 +1062,17 @@ const duckdb = new class {
|
|
|
1051
1062
|
for (const table of tables) {
|
|
1052
1063
|
consola.info(`Introspecting table schema: ${table.tableName}`);
|
|
1053
1064
|
try {
|
|
1054
|
-
const
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
nullable: true
|
|
1066
|
-
}, {
|
|
1067
|
-
name: "value",
|
|
1068
|
-
type: convertType(type.valueType),
|
|
1069
|
-
nullable: true
|
|
1070
|
-
});
|
|
1071
|
-
if (type instanceof DuckDBEnumType) return new EnumType(type.values);
|
|
1072
|
-
return type.toString();
|
|
1073
|
-
}
|
|
1074
|
-
table.columns = rows.map((row) => {
|
|
1075
|
-
return {
|
|
1076
|
-
name: row[0],
|
|
1077
|
-
type: row[1],
|
|
1078
|
-
nullable: row[2] !== "NO"
|
|
1079
|
-
};
|
|
1080
|
-
});
|
|
1065
|
+
const descRows = (await connection.runAndReadAll(`DESCRIBE ${table.tableName}`)).getRows();
|
|
1066
|
+
const nullabilityMap = /* @__PURE__ */ new Map();
|
|
1067
|
+
for (const row of descRows) nullabilityMap.set(row[0], row[2] !== "NO");
|
|
1068
|
+
const stream = await (await connection.prepare(`SELECT * FROM ${table.tableName} LIMIT 0`)).stream();
|
|
1069
|
+
const columnNames = stream.columnNames();
|
|
1070
|
+
const columnTypes = stream.columnTypes();
|
|
1071
|
+
table.columns = columnNames.map((name, index) => ({
|
|
1072
|
+
name,
|
|
1073
|
+
type: convertType(columnTypes[index]),
|
|
1074
|
+
nullable: nullabilityMap.get(name) ?? true
|
|
1075
|
+
}));
|
|
1081
1076
|
consola.debug(`Table ${table.tableName} columns:`, table.columns);
|
|
1082
1077
|
consola.success(`Introspected table: ${table.tableName} (${table.columns.length} columns)`);
|
|
1083
1078
|
} catch (error) {
|
|
@@ -1205,6 +1200,23 @@ const postgres = new class {
|
|
|
1205
1200
|
if (value.startsWith("'") && value.endsWith("'") || value.startsWith("\"") && value.endsWith("\"")) return value.slice(1, -1);
|
|
1206
1201
|
return value;
|
|
1207
1202
|
});
|
|
1203
|
+
if (statement.parameters.length > 0) try {
|
|
1204
|
+
await db.query(`DEALLOCATE ALL`);
|
|
1205
|
+
await db.query(`PREPARE sqg_param_check AS ${statement.sql}`);
|
|
1206
|
+
const paramTypeResult = await db.query(`SELECT unnest(parameter_types)::oid AS oid FROM pg_prepared_statements WHERE name = 'sqg_param_check'`);
|
|
1207
|
+
await db.query(`DEALLOCATE sqg_param_check`);
|
|
1208
|
+
if (paramTypeResult.rows.length === statement.parameters.length) {
|
|
1209
|
+
const paramTypes = /* @__PURE__ */ new Map();
|
|
1210
|
+
for (let i = 0; i < statement.parameters.length; i++) {
|
|
1211
|
+
const typeName = getTypeName(Number(paramTypeResult.rows[i].oid));
|
|
1212
|
+
paramTypes.set(statement.parameters[i].name, typeName);
|
|
1213
|
+
}
|
|
1214
|
+
query.parameterTypes = paramTypes;
|
|
1215
|
+
consola.debug("Parameter types:", Object.fromEntries(paramTypes));
|
|
1216
|
+
}
|
|
1217
|
+
} catch (e) {
|
|
1218
|
+
consola.debug(`Parameter type introspection failed for ${query.id}, using heuristic:`, e.message);
|
|
1219
|
+
}
|
|
1208
1220
|
let result;
|
|
1209
1221
|
try {
|
|
1210
1222
|
await db.query("BEGIN");
|
|
@@ -1964,6 +1976,24 @@ var JavaDuckDBArrowGenerator = class extends BaseGenerator {
|
|
|
1964
1976
|
|
|
1965
1977
|
//#endregion
|
|
1966
1978
|
//#region src/generators/typescript-generator.ts
|
|
1979
|
+
/** Resolve a ColumnType to its DuckDB type constant name (e.g. "VARCHAR", "INTEGER") for use in generated code. */
|
|
1980
|
+
function resolveElementType(baseType) {
|
|
1981
|
+
if (baseType instanceof ListType) return `new DuckDBListType(${resolveElementType(baseType.baseType)})`;
|
|
1982
|
+
return {
|
|
1983
|
+
VARCHAR: "VARCHAR",
|
|
1984
|
+
TEXT: "VARCHAR",
|
|
1985
|
+
INTEGER: "INTEGER",
|
|
1986
|
+
INT: "INTEGER",
|
|
1987
|
+
BIGINT: "BIGINT",
|
|
1988
|
+
DOUBLE: "DOUBLE",
|
|
1989
|
+
FLOAT: "FLOAT",
|
|
1990
|
+
BOOLEAN: "BOOLEAN",
|
|
1991
|
+
DATE: "DATE",
|
|
1992
|
+
TIMESTAMP: "TIMESTAMP",
|
|
1993
|
+
SMALLINT: "SMALLINT",
|
|
1994
|
+
TINYINT: "TINYINT"
|
|
1995
|
+
}[baseType.toString().toUpperCase()] || "VARCHAR";
|
|
1996
|
+
}
|
|
1967
1997
|
var TsGenerator = class extends BaseGenerator {
|
|
1968
1998
|
constructor(template) {
|
|
1969
1999
|
super(template, new TypeScriptTypeMapper());
|
|
@@ -1983,6 +2013,7 @@ var TsGenerator = class extends BaseGenerator {
|
|
|
1983
2013
|
async beforeGenerate(_projectDir, _gen, _queries, _tables) {
|
|
1984
2014
|
Handlebars.registerHelper("quote", (value) => this.quote(value));
|
|
1985
2015
|
Handlebars.registerHelper("appendMethod", (column) => {
|
|
2016
|
+
if (column.type instanceof ListType) return "List";
|
|
1986
2017
|
const typeStr = column.type?.toString().toUpperCase() || "";
|
|
1987
2018
|
if (typeStr === "INTEGER" || typeStr === "INT" || typeStr === "INT4" || typeStr === "SIGNED") return "Integer";
|
|
1988
2019
|
if (typeStr === "SMALLINT" || typeStr === "INT2" || typeStr === "SHORT") return "SmallInt";
|
|
@@ -2004,7 +2035,15 @@ var TsGenerator = class extends BaseGenerator {
|
|
|
2004
2035
|
if (typeStr === "INTERVAL") return "Interval";
|
|
2005
2036
|
return "Varchar";
|
|
2006
2037
|
});
|
|
2038
|
+
Handlebars.registerHelper("appendListTypeArg", (column) => {
|
|
2039
|
+
if (!(column.type instanceof ListType)) return "";
|
|
2040
|
+
return `, new DuckDBListType(${resolveElementType(column.type.baseType)})`;
|
|
2041
|
+
});
|
|
2007
2042
|
Handlebars.registerHelper("tsTypeForAppender", (column) => {
|
|
2043
|
+
if (column.type instanceof ListType) {
|
|
2044
|
+
const baseType$1 = "readonly DuckDBValue[]";
|
|
2045
|
+
return column.nullable ? `${baseType$1} | null` : baseType$1;
|
|
2046
|
+
}
|
|
2008
2047
|
const typeStr = column.type?.toString().toUpperCase() || "";
|
|
2009
2048
|
let baseType;
|
|
2010
2049
|
if (typeStr === "INTEGER" || typeStr === "INT" || typeStr === "INT4" || typeStr === "SMALLINT" || typeStr === "INT2" || typeStr === "TINYINT" || typeStr === "INT1" || typeStr === "UINTEGER" || typeStr === "UINT4" || typeStr === "USMALLINT" || typeStr === "UINT2" || typeStr === "UTINYINT" || typeStr === "UINT1" || typeStr === "DOUBLE" || typeStr === "FLOAT8" || typeStr === "FLOAT" || typeStr === "FLOAT4" || typeStr === "REAL") baseType = "number";
|
|
@@ -2145,6 +2184,21 @@ var TsDuckDBGenerator = class extends TsGenerator {
|
|
|
2145
2184
|
}
|
|
2146
2185
|
async beforeGenerate(projectDir, gen, queries, tables) {
|
|
2147
2186
|
await super.beforeGenerate(projectDir, gen, queries, tables);
|
|
2187
|
+
Handlebars.registerHelper("hasListParams", (queryHelper) => {
|
|
2188
|
+
const paramTypes = queryHelper.query.parameterTypes;
|
|
2189
|
+
if (!paramTypes) return false;
|
|
2190
|
+
for (const [, colType] of paramTypes) if (colType instanceof ListType) return true;
|
|
2191
|
+
return false;
|
|
2192
|
+
});
|
|
2193
|
+
Handlebars.registerHelper("bindStatements", (queryHelper) => {
|
|
2194
|
+
const paramNames = queryHelper.parameterNames;
|
|
2195
|
+
const paramTypes = queryHelper.query.parameterTypes;
|
|
2196
|
+
return paramNames.map((name, i) => {
|
|
2197
|
+
const colType = paramTypes?.get(name);
|
|
2198
|
+
if (colType instanceof ListType) return `stmt.bindList(${i + 1}, ${name}.items, new DuckDBListType(${resolveElementType(colType.baseType)}));`;
|
|
2199
|
+
return `stmt.bindValue(${i + 1}, ${name});`;
|
|
2200
|
+
}).join("\n ");
|
|
2201
|
+
});
|
|
2148
2202
|
Handlebars.registerHelper("tsType", (column) => {
|
|
2149
2203
|
const inlineType = (col) => {
|
|
2150
2204
|
const t = col.type;
|
|
@@ -2304,7 +2358,7 @@ var SqlQueryHelper = class {
|
|
|
2304
2358
|
get variables() {
|
|
2305
2359
|
return Array.from(this.query.variables.entries()).map(([name, value]) => ({
|
|
2306
2360
|
name,
|
|
2307
|
-
type: this.generator.mapParameterType(detectParameterType(value), false)
|
|
2361
|
+
type: this.generator.mapParameterType(this.query.parameterTypes?.get(name) ?? detectParameterType(value), false)
|
|
2308
2362
|
}));
|
|
2309
2363
|
}
|
|
2310
2364
|
get sqlQuery() {
|
|
@@ -2514,7 +2568,10 @@ async function writeGeneratedFile(projectDir, gen, generator, file, queries, tab
|
|
|
2514
2568
|
await generator.beforeGenerate(projectDir, gen, queries, tables);
|
|
2515
2569
|
const templatePath = join(dirname(new URL(import.meta.url).pathname), gen.template ?? generator.template);
|
|
2516
2570
|
const name = gen.name ?? basename(file, extname(file));
|
|
2517
|
-
const sourceFile = generateSourceFile(name, queries, tables, templatePath, generator, engine, gen.projectName ?? name,
|
|
2571
|
+
const sourceFile = generateSourceFile(name, queries, tables, templatePath, generator, engine, gen.projectName ?? name, {
|
|
2572
|
+
migrations: true,
|
|
2573
|
+
...gen.config
|
|
2574
|
+
});
|
|
2518
2575
|
if (writeToStdout) {
|
|
2519
2576
|
process.stdout.write(sourceFile);
|
|
2520
2577
|
if (!sourceFile.endsWith("\n")) process.stdout.write("\n");
|
|
@@ -2668,7 +2725,7 @@ async function processProject(projectPath) {
|
|
|
2668
2725
|
//#region src/mcp-server.ts
|
|
2669
2726
|
const server = new Server({
|
|
2670
2727
|
name: "sqg-mcp",
|
|
2671
|
-
version: process.env.npm_package_version ?? "0.
|
|
2728
|
+
version: process.env.npm_package_version ?? "0.10.0"
|
|
2672
2729
|
}, { capabilities: {
|
|
2673
2730
|
tools: {},
|
|
2674
2731
|
resources: {}
|
|
@@ -2997,7 +3054,7 @@ async function startMcpServer() {
|
|
|
2997
3054
|
|
|
2998
3055
|
//#endregion
|
|
2999
3056
|
//#region src/sqg.ts
|
|
3000
|
-
const version = process.env.npm_package_version ?? "0.
|
|
3057
|
+
const version = process.env.npm_package_version ?? "0.10.0";
|
|
3001
3058
|
const description = process.env.npm_package_description ?? "SQG - SQL Query Generator - Type-safe code generation from SQL (https://sqg.dev)";
|
|
3002
3059
|
consola.level = LogLevels.info;
|
|
3003
3060
|
const program = new Command().name("sqg").description(`${description}
|
|
@@ -17,11 +17,17 @@ import java.time.OffsetDateTime;
|
|
|
17
17
|
import java.time.OffsetTime;
|
|
18
18
|
import java.util.ArrayList;
|
|
19
19
|
import java.util.Arrays;
|
|
20
|
+
import java.util.Iterator;
|
|
20
21
|
import java.util.List;
|
|
21
22
|
import java.util.HashMap;
|
|
22
23
|
import java.util.Collections;
|
|
24
|
+
import java.util.NoSuchElementException;
|
|
25
|
+
import java.util.Spliterator;
|
|
26
|
+
import java.util.Spliterators;
|
|
23
27
|
import java.util.UUID;
|
|
24
28
|
import java.util.function.Function;
|
|
29
|
+
import java.util.stream.Stream;
|
|
30
|
+
import java.util.stream.StreamSupport;
|
|
25
31
|
{{#if tables.length}}
|
|
26
32
|
import org.duckdb.DuckDBAppender;
|
|
27
33
|
import org.duckdb.DuckDBConnection;
|
|
@@ -203,6 +209,36 @@ public class {{className}} {
|
|
|
203
209
|
{{> execute}}
|
|
204
210
|
}
|
|
205
211
|
}
|
|
212
|
+
{{#if isQuery}}{{#unless isOne}}
|
|
213
|
+
public Stream<{{rowType}}> {{functionName}}Stream({{#each variables}}{{{type}}} {{name}}{{#unless @last}}, {{/unless}}{{/each}}) throws SQLException {
|
|
214
|
+
var stmt = connection.prepareStatement({{{partsToString sqlQueryParts}}});
|
|
215
|
+
{{#each parameterNames}}stmt.setObject({{plusOne @index}}, {{this}});
|
|
216
|
+
{{/each}}
|
|
217
|
+
var rs = stmt.executeQuery();
|
|
218
|
+
var iter = new Iterator<{{rowType}}>() {
|
|
219
|
+
private Boolean hasNext = null;
|
|
220
|
+
public boolean hasNext() {
|
|
221
|
+
if (hasNext == null) {
|
|
222
|
+
try { hasNext = rs.next(); }
|
|
223
|
+
catch (SQLException e) { throw new RuntimeException(e); }
|
|
224
|
+
}
|
|
225
|
+
return hasNext;
|
|
226
|
+
}
|
|
227
|
+
public {{rowType}} next() {
|
|
228
|
+
if (!hasNext()) throw new NoSuchElementException();
|
|
229
|
+
hasNext = null;
|
|
230
|
+
try { return {{> readRow}}; }
|
|
231
|
+
catch (SQLException e) { throw new RuntimeException(e); }
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
return StreamSupport.stream(
|
|
235
|
+
Spliterators.spliteratorUnknownSize(iter, Spliterator.ORDERED), false
|
|
236
|
+
).onClose(() -> {
|
|
237
|
+
try { rs.close(); stmt.close(); }
|
|
238
|
+
catch (SQLException e) { throw new RuntimeException(e); }
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
{{/unless}}{{/if}}
|
|
206
242
|
{{/unless}}
|
|
207
243
|
{{/each}}
|
|
208
244
|
|
|
@@ -220,7 +256,7 @@ public class {{className}} {
|
|
|
220
256
|
|
|
221
257
|
{{#each tables}}
|
|
222
258
|
/** Row type for {{tableName}} appender */
|
|
223
|
-
public record {{rowTypeName}}({{#each columns}}{{mapType this}} {{name}}{{#unless @last}}, {{/unless}}{{/each}}) {}
|
|
259
|
+
public record {{rowTypeName}}({{#each columns}}{{{mapType this}}} {{name}}{{#unless @last}}, {{/unless}}{{/each}}) {}
|
|
224
260
|
|
|
225
261
|
/** Appender for bulk inserts into {{tableName}} */
|
|
226
262
|
public static class {{className}} implements AutoCloseable {
|
|
@@ -246,7 +282,7 @@ public static class {{className}} implements AutoCloseable {
|
|
|
246
282
|
}
|
|
247
283
|
|
|
248
284
|
/** Append a single row with individual values */
|
|
249
|
-
public {{className}} append({{#each columns}}{{mapType this}} {{name}}{{#unless @last}}, {{/unless}}{{/each}}) throws SQLException {
|
|
285
|
+
public {{className}} append({{#each columns}}{{{mapType this}}} {{name}}{{#unless @last}}, {{/unless}}{{/each}}) throws SQLException {
|
|
250
286
|
appender.beginRow();
|
|
251
287
|
{{#each columns}}
|
|
252
288
|
appender.append({{name}});
|
package/dist/templates/turso.hbs
CHANGED
|
@@ -33,36 +33,32 @@ export class {{className}} {
|
|
|
33
33
|
|
|
34
34
|
{{#if config.migrations}}
|
|
35
35
|
static async applyMigrations(db: Database, projectName = '{{projectName}}'): Promise<void> {
|
|
36
|
-
const createStmt =
|
|
36
|
+
const createStmt = db.prepare(`CREATE TABLE IF NOT EXISTS _sqg_migrations (
|
|
37
37
|
project TEXT NOT NULL,
|
|
38
38
|
migration_id TEXT NOT NULL,
|
|
39
39
|
applied_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
40
40
|
PRIMARY KEY (project, migration_id)
|
|
41
41
|
)`);
|
|
42
42
|
await createStmt.run();
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
];
|
|
43
|
+
const selectStmt = db.prepare('SELECT migration_id FROM _sqg_migrations WHERE project = ?');
|
|
44
|
+
const rows = await selectStmt.all(projectName) as { migration_id: string }[];
|
|
45
|
+
const applied = new Set(rows.map(r => r.migration_id));
|
|
46
|
+
const migrations: [string, string][] = [
|
|
47
|
+
{{#each migrations}}
|
|
48
|
+
['{{{id}}}', {{{quote sqlQuery}}}],
|
|
49
|
+
{{/each}}
|
|
50
|
+
];
|
|
51
|
+
const applyFn = db.transaction(async () => {
|
|
53
52
|
for (const [id, sql] of migrations) {
|
|
54
53
|
if (!applied.has(id)) {
|
|
55
|
-
const execStmt =
|
|
54
|
+
const execStmt = db.prepare(sql);
|
|
56
55
|
await execStmt.run();
|
|
57
|
-
const insertStmt =
|
|
56
|
+
const insertStmt = db.prepare('INSERT INTO _sqg_migrations (project, migration_id) VALUES (?, ?)');
|
|
58
57
|
await insertStmt.run(projectName, id);
|
|
59
58
|
}
|
|
60
59
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
await tx.rollback();
|
|
64
|
-
throw e;
|
|
65
|
-
}
|
|
60
|
+
});
|
|
61
|
+
await applyFn();
|
|
66
62
|
}
|
|
67
63
|
{{/if}}
|
|
68
64
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// {{generatedComment}}
|
|
2
|
-
import type
|
|
2
|
+
import { DuckDBListType, listValue, VARCHAR, INTEGER, BIGINT, DOUBLE, FLOAT, BOOLEAN, SMALLINT, TINYINT, type DuckDBConnection, type DuckDBMaterializedResult, type DuckDBAppender, type DuckDBDateValue, type DuckDBTimeValue, type DuckDBTimestampValue, type DuckDBBlobValue, type DuckDBValue } from "@duckdb/node-api";
|
|
3
3
|
|
|
4
4
|
export class {{className}} {
|
|
5
5
|
|
|
@@ -60,6 +60,28 @@ export class {{className}} {
|
|
|
60
60
|
{{#unless skipGenerateFunction}}
|
|
61
61
|
async {{functionName}}({{#each variables}}{{name}}: {{type}}{{#unless @last}}, {{/unless}}{{/each}}): Promise<{{> returnType }}> {
|
|
62
62
|
const sql = {{{quote sqlQuery}}};
|
|
63
|
+
{{#if (hasListParams this)}}
|
|
64
|
+
const stmt = await this.conn.prepare(sql);
|
|
65
|
+
{{{bindStatements this}}}
|
|
66
|
+
{{#if isQuery}}
|
|
67
|
+
const reader = await stmt.runAndReadAll();
|
|
68
|
+
{{#if isPluck}}
|
|
69
|
+
{{#if isOne}}
|
|
70
|
+
return reader.getRows()[0]?.[0] as {{> rowType}} | undefined;
|
|
71
|
+
{{else}}
|
|
72
|
+
return reader.getRows().map((row) => row[0] as {{> rowType}});
|
|
73
|
+
{{/if}}
|
|
74
|
+
{{else}}
|
|
75
|
+
{{#if isOne}}
|
|
76
|
+
return reader.getRowObjects()[0] as {{> rowType}} | undefined;
|
|
77
|
+
{{else}}
|
|
78
|
+
return reader.getRowObjects() as {{> rowType}}[];
|
|
79
|
+
{{/if}}
|
|
80
|
+
{{/if}}
|
|
81
|
+
{{else}}
|
|
82
|
+
return await stmt.run();
|
|
83
|
+
{{/if}}
|
|
84
|
+
{{else}}
|
|
63
85
|
{{#if isQuery}}
|
|
64
86
|
const reader = await this.conn.runAndReadAll(sql,[{{> params}}]);
|
|
65
87
|
{{#if isPluck}}
|
|
@@ -78,6 +100,7 @@ export class {{className}} {
|
|
|
78
100
|
{{else}}
|
|
79
101
|
return await this.conn.run(sql,[{{> params}}]);
|
|
80
102
|
{{/if}}
|
|
103
|
+
{{/if}}
|
|
81
104
|
}
|
|
82
105
|
{{/unless}}
|
|
83
106
|
|
|
@@ -113,10 +136,10 @@ export class {{className}} {
|
|
|
113
136
|
if (row.{{name}} === null || row.{{name}} === undefined) {
|
|
114
137
|
this.appender.appendNull();
|
|
115
138
|
} else {
|
|
116
|
-
this.appender.append{{appendMethod this}}(row.{{name}});
|
|
139
|
+
this.appender.append{{appendMethod this}}(row.{{name}}{{{appendListTypeArg this}}});
|
|
117
140
|
}
|
|
118
141
|
{{else}}
|
|
119
|
-
this.appender.append{{appendMethod this}}(row.{{name}});
|
|
142
|
+
this.appender.append{{appendMethod this}}(row.{{name}}{{{appendListTypeArg this}}});
|
|
120
143
|
{{/if}}
|
|
121
144
|
{{/each}}
|
|
122
145
|
this.appender.endRow();
|