@sqg/sqg 0.9.0 → 0.11.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 +113 -57
- package/dist/templates/java-jdbc.hbs +42 -4
- 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,7 +680,7 @@ var EnumType = class {
|
|
|
680
680
|
var SQLQuery = class {
|
|
681
681
|
columns;
|
|
682
682
|
allColumns;
|
|
683
|
-
/** Database-reported parameter types (variable name → SQL type
|
|
683
|
+
/** Database-reported parameter types (variable name → SQL type), set by database adapters */
|
|
684
684
|
parameterTypes;
|
|
685
685
|
constructor(filename, id, rawQuery, queryAnonymous, queryNamed, queryPositional, type, isOne, isPluck, variables, config) {
|
|
686
686
|
this.filename = filename;
|
|
@@ -966,6 +966,25 @@ async function initializeDatabase(queries, execQueries) {
|
|
|
966
966
|
|
|
967
967
|
//#endregion
|
|
968
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
|
+
}
|
|
969
988
|
const duckdb = new class {
|
|
970
989
|
db;
|
|
971
990
|
connection;
|
|
@@ -1005,6 +1024,15 @@ const duckdb = new class {
|
|
|
1005
1024
|
return ` ${part.value} `;
|
|
1006
1025
|
}).join("");
|
|
1007
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
|
+
}
|
|
1008
1036
|
for (let i = 0; i < stmt.parameterCount; i++) stmt.bindValue(i + 1, statement.parameters[i].value);
|
|
1009
1037
|
if (query.isQuery) {
|
|
1010
1038
|
const result = await stmt.stream();
|
|
@@ -1012,25 +1040,6 @@ const duckdb = new class {
|
|
|
1012
1040
|
const columnTypes = result.columnTypes();
|
|
1013
1041
|
consola.debug("Columns:", columnNames);
|
|
1014
1042
|
consola.debug("Types:", columnTypes.map((t) => `${t.toString()} / ${t.constructor.name}`));
|
|
1015
|
-
function convertType(type) {
|
|
1016
|
-
if (type instanceof DuckDBListType) return new ListType(convertType(type.valueType));
|
|
1017
|
-
if (type instanceof DuckDBStructType) return new StructType(type.entryTypes.map((t, index) => ({
|
|
1018
|
-
name: type.entryNames[index],
|
|
1019
|
-
type: convertType(t),
|
|
1020
|
-
nullable: true
|
|
1021
|
-
})));
|
|
1022
|
-
if (type instanceof DuckDBMapType) return new MapType({
|
|
1023
|
-
name: "key",
|
|
1024
|
-
type: convertType(type.keyType),
|
|
1025
|
-
nullable: true
|
|
1026
|
-
}, {
|
|
1027
|
-
name: "value",
|
|
1028
|
-
type: convertType(type.valueType),
|
|
1029
|
-
nullable: true
|
|
1030
|
-
});
|
|
1031
|
-
if (type instanceof DuckDBEnumType) return new EnumType(type.values);
|
|
1032
|
-
return type.toString();
|
|
1033
|
-
}
|
|
1034
1043
|
query.columns = columnNames.map((name, index) => ({
|
|
1035
1044
|
name,
|
|
1036
1045
|
type: convertType(columnTypes[index]),
|
|
@@ -1053,33 +1062,17 @@ const duckdb = new class {
|
|
|
1053
1062
|
for (const table of tables) {
|
|
1054
1063
|
consola.info(`Introspecting table schema: ${table.tableName}`);
|
|
1055
1064
|
try {
|
|
1056
|
-
const
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
nullable: true
|
|
1068
|
-
}, {
|
|
1069
|
-
name: "value",
|
|
1070
|
-
type: convertType(type.valueType),
|
|
1071
|
-
nullable: true
|
|
1072
|
-
});
|
|
1073
|
-
if (type instanceof DuckDBEnumType) return new EnumType(type.values);
|
|
1074
|
-
return type.toString();
|
|
1075
|
-
}
|
|
1076
|
-
table.columns = rows.map((row) => {
|
|
1077
|
-
return {
|
|
1078
|
-
name: row[0],
|
|
1079
|
-
type: row[1],
|
|
1080
|
-
nullable: row[2] !== "NO"
|
|
1081
|
-
};
|
|
1082
|
-
});
|
|
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
|
+
}));
|
|
1083
1076
|
consola.debug(`Table ${table.tableName} columns:`, table.columns);
|
|
1084
1077
|
consola.success(`Introspected table: ${table.tableName} (${table.columns.length} columns)`);
|
|
1085
1078
|
} catch (error) {
|
|
@@ -1208,10 +1201,10 @@ const postgres = new class {
|
|
|
1208
1201
|
return value;
|
|
1209
1202
|
});
|
|
1210
1203
|
if (statement.parameters.length > 0) try {
|
|
1211
|
-
await db.query(
|
|
1204
|
+
await db.query("DEALLOCATE ALL");
|
|
1212
1205
|
await db.query(`PREPARE sqg_param_check AS ${statement.sql}`);
|
|
1213
1206
|
const paramTypeResult = await db.query(`SELECT unnest(parameter_types)::oid AS oid FROM pg_prepared_statements WHERE name = 'sqg_param_check'`);
|
|
1214
|
-
await db.query(
|
|
1207
|
+
await db.query("DEALLOCATE sqg_param_check");
|
|
1215
1208
|
if (paramTypeResult.rows.length === statement.parameters.length) {
|
|
1216
1209
|
const paramTypes = /* @__PURE__ */ new Map();
|
|
1217
1210
|
for (let i = 0; i < statement.parameters.length; i++) {
|
|
@@ -1466,7 +1459,7 @@ var JavaTypeMapper = class JavaTypeMapper extends TypeMapper {
|
|
|
1466
1459
|
TIMESTAMP_S: "Instant",
|
|
1467
1460
|
TIMESTAMP_MS: "Instant",
|
|
1468
1461
|
TIMESTAMP_NS: "Instant",
|
|
1469
|
-
"TIMESTAMP WITH TIME ZONE": "
|
|
1462
|
+
"TIMESTAMP WITH TIME ZONE": "OffsetDateTime",
|
|
1470
1463
|
UUID: "UUID",
|
|
1471
1464
|
INTERVAL: "String",
|
|
1472
1465
|
BIT: "String",
|
|
@@ -1607,6 +1600,7 @@ var JavaTypeMapper = class JavaTypeMapper extends TypeMapper {
|
|
|
1607
1600
|
const upperType = column.type?.toString().toUpperCase() ?? "";
|
|
1608
1601
|
if (upperType === "TIMESTAMP" || upperType === "DATETIME") return `toLocalDateTime((java.sql.Timestamp)${value})`;
|
|
1609
1602
|
if (upperType === "TIMESTAMPTZ") return `toOffsetDateTime((java.sql.Timestamp)${value})`;
|
|
1603
|
+
if (upperType === "TIMESTAMP WITH TIME ZONE") return `(OffsetDateTime)${value}`;
|
|
1610
1604
|
if (upperType === "DATE") return `toLocalDate((java.sql.Date)${value})`;
|
|
1611
1605
|
if (upperType === "TIME") return `toLocalTime((java.sql.Time)${value})`;
|
|
1612
1606
|
if (upperType.startsWith("_")) {
|
|
@@ -1864,6 +1858,9 @@ var JavaGenerator = class extends BaseGenerator {
|
|
|
1864
1858
|
}, " ");
|
|
1865
1859
|
return queryHelper.typeMapper.getDeclarations(query.allColumns);
|
|
1866
1860
|
});
|
|
1861
|
+
Handlebars.registerHelper("appenderType", (column) => {
|
|
1862
|
+
return this.mapType(column);
|
|
1863
|
+
});
|
|
1867
1864
|
Handlebars.registerHelper("readColumns", (queryHelper) => {
|
|
1868
1865
|
const query = queryHelper.query;
|
|
1869
1866
|
if (queryHelper.isPluck) return this.readColumn({
|
|
@@ -1983,6 +1980,24 @@ var JavaDuckDBArrowGenerator = class extends BaseGenerator {
|
|
|
1983
1980
|
|
|
1984
1981
|
//#endregion
|
|
1985
1982
|
//#region src/generators/typescript-generator.ts
|
|
1983
|
+
/** Resolve a ColumnType to its DuckDB type constant name (e.g. "VARCHAR", "INTEGER") for use in generated code. */
|
|
1984
|
+
function resolveElementType(baseType) {
|
|
1985
|
+
if (baseType instanceof ListType) return `new DuckDBListType(${resolveElementType(baseType.baseType)})`;
|
|
1986
|
+
return {
|
|
1987
|
+
VARCHAR: "VARCHAR",
|
|
1988
|
+
TEXT: "VARCHAR",
|
|
1989
|
+
INTEGER: "INTEGER",
|
|
1990
|
+
INT: "INTEGER",
|
|
1991
|
+
BIGINT: "BIGINT",
|
|
1992
|
+
DOUBLE: "DOUBLE",
|
|
1993
|
+
FLOAT: "FLOAT",
|
|
1994
|
+
BOOLEAN: "BOOLEAN",
|
|
1995
|
+
DATE: "DATE",
|
|
1996
|
+
TIMESTAMP: "TIMESTAMP",
|
|
1997
|
+
SMALLINT: "SMALLINT",
|
|
1998
|
+
TINYINT: "TINYINT"
|
|
1999
|
+
}[baseType.toString().toUpperCase()] || "VARCHAR";
|
|
2000
|
+
}
|
|
1986
2001
|
var TsGenerator = class extends BaseGenerator {
|
|
1987
2002
|
constructor(template) {
|
|
1988
2003
|
super(template, new TypeScriptTypeMapper());
|
|
@@ -2002,6 +2017,7 @@ var TsGenerator = class extends BaseGenerator {
|
|
|
2002
2017
|
async beforeGenerate(_projectDir, _gen, _queries, _tables) {
|
|
2003
2018
|
Handlebars.registerHelper("quote", (value) => this.quote(value));
|
|
2004
2019
|
Handlebars.registerHelper("appendMethod", (column) => {
|
|
2020
|
+
if (column.type instanceof ListType) return "List";
|
|
2005
2021
|
const typeStr = column.type?.toString().toUpperCase() || "";
|
|
2006
2022
|
if (typeStr === "INTEGER" || typeStr === "INT" || typeStr === "INT4" || typeStr === "SIGNED") return "Integer";
|
|
2007
2023
|
if (typeStr === "SMALLINT" || typeStr === "INT2" || typeStr === "SHORT") return "SmallInt";
|
|
@@ -2023,7 +2039,15 @@ var TsGenerator = class extends BaseGenerator {
|
|
|
2023
2039
|
if (typeStr === "INTERVAL") return "Interval";
|
|
2024
2040
|
return "Varchar";
|
|
2025
2041
|
});
|
|
2042
|
+
Handlebars.registerHelper("appendListTypeArg", (column) => {
|
|
2043
|
+
if (!(column.type instanceof ListType)) return "";
|
|
2044
|
+
return `, new DuckDBListType(${resolveElementType(column.type.baseType)})`;
|
|
2045
|
+
});
|
|
2026
2046
|
Handlebars.registerHelper("tsTypeForAppender", (column) => {
|
|
2047
|
+
if (column.type instanceof ListType) {
|
|
2048
|
+
const baseType$1 = "readonly DuckDBValue[]";
|
|
2049
|
+
return column.nullable ? `${baseType$1} | null` : baseType$1;
|
|
2050
|
+
}
|
|
2027
2051
|
const typeStr = column.type?.toString().toUpperCase() || "";
|
|
2028
2052
|
let baseType;
|
|
2029
2053
|
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";
|
|
@@ -2164,6 +2188,21 @@ var TsDuckDBGenerator = class extends TsGenerator {
|
|
|
2164
2188
|
}
|
|
2165
2189
|
async beforeGenerate(projectDir, gen, queries, tables) {
|
|
2166
2190
|
await super.beforeGenerate(projectDir, gen, queries, tables);
|
|
2191
|
+
Handlebars.registerHelper("hasListParams", (queryHelper) => {
|
|
2192
|
+
const paramTypes = queryHelper.query.parameterTypes;
|
|
2193
|
+
if (!paramTypes) return false;
|
|
2194
|
+
for (const [, colType] of paramTypes) if (colType instanceof ListType) return true;
|
|
2195
|
+
return false;
|
|
2196
|
+
});
|
|
2197
|
+
Handlebars.registerHelper("bindStatements", (queryHelper) => {
|
|
2198
|
+
const paramNames = queryHelper.parameterNames;
|
|
2199
|
+
const paramTypes = queryHelper.query.parameterTypes;
|
|
2200
|
+
return paramNames.map((name, i) => {
|
|
2201
|
+
const colType = paramTypes?.get(name);
|
|
2202
|
+
if (colType instanceof ListType) return `stmt.bindList(${i + 1}, ${name}.items, new DuckDBListType(${resolveElementType(colType.baseType)}));`;
|
|
2203
|
+
return `stmt.bindValue(${i + 1}, ${name});`;
|
|
2204
|
+
}).join("\n ");
|
|
2205
|
+
});
|
|
2167
2206
|
Handlebars.registerHelper("tsType", (column) => {
|
|
2168
2207
|
const inlineType = (col) => {
|
|
2169
2208
|
const t = col.type;
|
|
@@ -2311,10 +2350,24 @@ var SqlQueryHelper = class {
|
|
|
2311
2350
|
}
|
|
2312
2351
|
get parameters() {
|
|
2313
2352
|
const vars = new Map(this.variables.map((param) => [param.name, param.type]));
|
|
2314
|
-
return this.statement.parameters.map((param) =>
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2353
|
+
return this.statement.parameters.map((param) => {
|
|
2354
|
+
const rawType = this.query.parameterTypes?.get(param.name);
|
|
2355
|
+
let isArray = false;
|
|
2356
|
+
let arrayBaseType = null;
|
|
2357
|
+
if (rawType instanceof ListType) {
|
|
2358
|
+
isArray = true;
|
|
2359
|
+
arrayBaseType = rawType.baseType.toString();
|
|
2360
|
+
} else if (typeof rawType === "string" && rawType.startsWith("_")) {
|
|
2361
|
+
isArray = true;
|
|
2362
|
+
arrayBaseType = rawType.substring(1);
|
|
2363
|
+
}
|
|
2364
|
+
return {
|
|
2365
|
+
name: param.name,
|
|
2366
|
+
type: vars.get(param.name),
|
|
2367
|
+
isArray,
|
|
2368
|
+
arrayBaseType
|
|
2369
|
+
};
|
|
2370
|
+
});
|
|
2318
2371
|
}
|
|
2319
2372
|
get columns() {
|
|
2320
2373
|
if (!(this.query.allColumns.type instanceof StructType)) throw new Error(`Expected StructType ${this.query.allColumns.type}`);
|
|
@@ -2533,7 +2586,10 @@ async function writeGeneratedFile(projectDir, gen, generator, file, queries, tab
|
|
|
2533
2586
|
await generator.beforeGenerate(projectDir, gen, queries, tables);
|
|
2534
2587
|
const templatePath = join(dirname(new URL(import.meta.url).pathname), gen.template ?? generator.template);
|
|
2535
2588
|
const name = gen.name ?? basename(file, extname(file));
|
|
2536
|
-
const sourceFile = generateSourceFile(name, queries, tables, templatePath, generator, engine, gen.projectName ?? name,
|
|
2589
|
+
const sourceFile = generateSourceFile(name, queries, tables, templatePath, generator, engine, gen.projectName ?? name, {
|
|
2590
|
+
migrations: true,
|
|
2591
|
+
...gen.config
|
|
2592
|
+
});
|
|
2537
2593
|
if (writeToStdout) {
|
|
2538
2594
|
process.stdout.write(sourceFile);
|
|
2539
2595
|
if (!sourceFile.endsWith("\n")) process.stdout.write("\n");
|
|
@@ -2687,7 +2743,7 @@ async function processProject(projectPath) {
|
|
|
2687
2743
|
//#region src/mcp-server.ts
|
|
2688
2744
|
const server = new Server({
|
|
2689
2745
|
name: "sqg-mcp",
|
|
2690
|
-
version: process.env.npm_package_version ?? "0.
|
|
2746
|
+
version: process.env.npm_package_version ?? "0.11.0"
|
|
2691
2747
|
}, { capabilities: {
|
|
2692
2748
|
tools: {},
|
|
2693
2749
|
resources: {}
|
|
@@ -3016,7 +3072,7 @@ async function startMcpServer() {
|
|
|
3016
3072
|
|
|
3017
3073
|
//#endregion
|
|
3018
3074
|
//#region src/sqg.ts
|
|
3019
|
-
const version = process.env.npm_package_version ?? "0.
|
|
3075
|
+
const version = process.env.npm_package_version ?? "0.11.0";
|
|
3020
3076
|
const description = process.env.npm_package_description ?? "SQG - SQL Query Generator - Type-safe code generation from SQL (https://sqg.dev)";
|
|
3021
3077
|
consola.level = LogLevels.info;
|
|
3022
3078
|
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,37 @@ 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 parameters}}{{#if isArray}}stmt.setArray({{plusOne @index}}, connection.createArrayOf("{{arrayBaseType}}", {{name}}.toArray()));
|
|
216
|
+
{{else}}stmt.setObject({{plusOne @index}}, {{name}});
|
|
217
|
+
{{/if}}{{/each}}
|
|
218
|
+
var rs = stmt.executeQuery();
|
|
219
|
+
var iter = new Iterator<{{rowType}}>() {
|
|
220
|
+
private Boolean hasNext = null;
|
|
221
|
+
public boolean hasNext() {
|
|
222
|
+
if (hasNext == null) {
|
|
223
|
+
try { hasNext = rs.next(); }
|
|
224
|
+
catch (SQLException e) { throw new RuntimeException(e); }
|
|
225
|
+
}
|
|
226
|
+
return hasNext;
|
|
227
|
+
}
|
|
228
|
+
public {{rowType}} next() {
|
|
229
|
+
if (!hasNext()) throw new NoSuchElementException();
|
|
230
|
+
hasNext = null;
|
|
231
|
+
try { return {{> readRow}}; }
|
|
232
|
+
catch (SQLException e) { throw new RuntimeException(e); }
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
return StreamSupport.stream(
|
|
236
|
+
Spliterators.spliteratorUnknownSize(iter, Spliterator.ORDERED), false
|
|
237
|
+
).onClose(() -> {
|
|
238
|
+
try { rs.close(); stmt.close(); }
|
|
239
|
+
catch (SQLException e) { throw new RuntimeException(e); }
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
{{/unless}}{{/if}}
|
|
206
243
|
{{/unless}}
|
|
207
244
|
{{/each}}
|
|
208
245
|
|
|
@@ -220,7 +257,7 @@ public class {{className}} {
|
|
|
220
257
|
|
|
221
258
|
{{#each tables}}
|
|
222
259
|
/** Row type for {{tableName}} appender */
|
|
223
|
-
public record {{rowTypeName}}({{#each columns}}{{
|
|
260
|
+
public record {{rowTypeName}}({{#each columns}}{{{appenderType this}}} {{name}}{{#unless @last}}, {{/unless}}{{/each}}) {}
|
|
224
261
|
|
|
225
262
|
/** Appender for bulk inserts into {{tableName}} */
|
|
226
263
|
public static class {{className}} implements AutoCloseable {
|
|
@@ -246,7 +283,7 @@ public static class {{className}} implements AutoCloseable {
|
|
|
246
283
|
}
|
|
247
284
|
|
|
248
285
|
/** Append a single row with individual values */
|
|
249
|
-
public {{className}} append({{#each columns}}{{
|
|
286
|
+
public {{className}} append({{#each columns}}{{{appenderType this}}} {{name}}{{#unless @last}}, {{/unless}}{{/each}}) throws SQLException {
|
|
250
287
|
appender.beginRow();
|
|
251
288
|
{{#each columns}}
|
|
252
289
|
appender.append({{name}});
|
|
@@ -291,8 +328,9 @@ int
|
|
|
291
328
|
{{/inline~}}
|
|
292
329
|
|
|
293
330
|
{{#*inline "execute"}}
|
|
294
|
-
{{#each
|
|
295
|
-
{{
|
|
331
|
+
{{#each parameters}}{{#if isArray}}stmt.setArray({{plusOne @index}}, connection.createArrayOf("{{arrayBaseType}}", {{name}}.toArray()));
|
|
332
|
+
{{else}}stmt.setObject({{plusOne @index}}, {{name}});
|
|
333
|
+
{{/if}}{{/each}}
|
|
296
334
|
{{#if isQuery}}
|
|
297
335
|
try(var rs = stmt.executeQuery()) {
|
|
298
336
|
{{#if isOne}}
|
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();
|