@sqg/sqg 0.3.0 → 0.4.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 +42 -23
- package/dist/templates/java-duckdb-arrow.hbs +2 -2
- package/package.json +1 -1
package/dist/sqg.mjs
CHANGED
|
@@ -26,7 +26,7 @@ import estree from "prettier/plugins/estree";
|
|
|
26
26
|
* This file enables self-documenting CLI help and validation.
|
|
27
27
|
*/
|
|
28
28
|
/** Supported database engines */
|
|
29
|
-
const
|
|
29
|
+
const DB_ENGINES = [
|
|
30
30
|
"sqlite",
|
|
31
31
|
"duckdb",
|
|
32
32
|
"postgres"
|
|
@@ -114,7 +114,7 @@ function formatGeneratorsHelp() {
|
|
|
114
114
|
* Format engines for CLI help output
|
|
115
115
|
*/
|
|
116
116
|
function formatEnginesHelp() {
|
|
117
|
-
return
|
|
117
|
+
return DB_ENGINES.map((e) => ` ${e}`).join("\n");
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
//#endregion
|
|
@@ -406,11 +406,15 @@ function parseSQLQueries(filePath, extraVariables) {
|
|
|
406
406
|
const queries = [];
|
|
407
407
|
const tables = [];
|
|
408
408
|
const cursor = parser.parse(content).cursor();
|
|
409
|
+
function getLineNumber(position) {
|
|
410
|
+
return content.slice(0, position).split("\n").length;
|
|
411
|
+
}
|
|
409
412
|
function getStr(nodeName, optional = false) {
|
|
410
413
|
const node = cursor.node.getChild(nodeName);
|
|
411
414
|
if (!node) {
|
|
412
415
|
if (optional) return;
|
|
413
|
-
|
|
416
|
+
const lineNumber = getLineNumber(cursor.node.from);
|
|
417
|
+
throw new Error(`Node '${nodeName}' not found at line ${lineNumber}`);
|
|
414
418
|
}
|
|
415
419
|
return nodeStr(node);
|
|
416
420
|
}
|
|
@@ -1328,6 +1332,9 @@ var BaseGenerator = class {
|
|
|
1328
1332
|
isCompatibleWith(_engine) {
|
|
1329
1333
|
return true;
|
|
1330
1334
|
}
|
|
1335
|
+
supportsAppenders(_engine) {
|
|
1336
|
+
return false;
|
|
1337
|
+
}
|
|
1331
1338
|
functionReturnType(query) {
|
|
1332
1339
|
if (query.isOne) return this.rowType(query);
|
|
1333
1340
|
return this.typeMapper.formatListType(this.rowType(query));
|
|
@@ -1351,6 +1358,9 @@ var JavaGenerator = class extends BaseGenerator {
|
|
|
1351
1358
|
super(template, new JavaTypeMapper());
|
|
1352
1359
|
this.template = template;
|
|
1353
1360
|
}
|
|
1361
|
+
supportsAppenders(engine) {
|
|
1362
|
+
return engine === "duckdb";
|
|
1363
|
+
}
|
|
1354
1364
|
getFunctionName(id) {
|
|
1355
1365
|
return camelCase$1(id);
|
|
1356
1366
|
}
|
|
@@ -1450,11 +1460,14 @@ var JavaDuckDBArrowGenerator = class extends BaseGenerator {
|
|
|
1450
1460
|
generator: "java/jdbc",
|
|
1451
1461
|
output: gen.output,
|
|
1452
1462
|
config: gen.config
|
|
1453
|
-
}, this.javaGenerator, name, q, tables);
|
|
1463
|
+
}, this.javaGenerator, name, q, tables, "duckdb");
|
|
1454
1464
|
}
|
|
1455
1465
|
isCompatibleWith(engine) {
|
|
1456
1466
|
return engine === "duckdb";
|
|
1457
1467
|
}
|
|
1468
|
+
supportsAppenders(_engine) {
|
|
1469
|
+
return true;
|
|
1470
|
+
}
|
|
1458
1471
|
getFilename(sqlFileName) {
|
|
1459
1472
|
return this.javaGenerator.getFilename(sqlFileName);
|
|
1460
1473
|
}
|
|
@@ -1462,25 +1475,28 @@ var JavaDuckDBArrowGenerator = class extends BaseGenerator {
|
|
|
1462
1475
|
return this.javaGenerator.getClassName(name);
|
|
1463
1476
|
}
|
|
1464
1477
|
mapType(column) {
|
|
1465
|
-
const { type
|
|
1478
|
+
const { type } = column;
|
|
1466
1479
|
if (typeof type === "string") {
|
|
1467
1480
|
const mappedType = {
|
|
1468
1481
|
INTEGER: "IntVector",
|
|
1482
|
+
BIGINT: "BigIntVector",
|
|
1469
1483
|
BOOLEAN: "BitVector",
|
|
1470
1484
|
DOUBLE: "Float8Vector",
|
|
1471
1485
|
FLOAT: "Float4Vector",
|
|
1472
1486
|
VARCHAR: "VarCharVector",
|
|
1473
|
-
TEXT: "VarCharVector"
|
|
1487
|
+
TEXT: "VarCharVector",
|
|
1488
|
+
TIMESTAMP: "TimeStampVector",
|
|
1489
|
+
DATE: "DateDayVector",
|
|
1490
|
+
TIME: "TimeMicroVector"
|
|
1474
1491
|
}[type.toUpperCase()];
|
|
1475
1492
|
if (!mappedType) consola.warn("(duckdb-arrow) Mapped type is unknown:", type);
|
|
1476
1493
|
return mappedType ?? "Object";
|
|
1477
1494
|
}
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
return this.typeMapper.getTypeName(mockColumn);
|
|
1495
|
+
if (type instanceof ListType) return "ListVector";
|
|
1496
|
+
if (type instanceof StructType) return "StructVector";
|
|
1497
|
+
if (type instanceof MapType) return "MapVector";
|
|
1498
|
+
consola.warn("(duckdb-arrow) Unknown complex type:", type);
|
|
1499
|
+
return "Object";
|
|
1484
1500
|
}
|
|
1485
1501
|
mapParameterType(type, nullable) {
|
|
1486
1502
|
return this.typeMapper.getTypeName({
|
|
@@ -1688,6 +1704,9 @@ var TsDuckDBGenerator = class extends TsGenerator {
|
|
|
1688
1704
|
constructor(template) {
|
|
1689
1705
|
super(template);
|
|
1690
1706
|
}
|
|
1707
|
+
supportsAppenders(_engine) {
|
|
1708
|
+
return true;
|
|
1709
|
+
}
|
|
1691
1710
|
async beforeGenerate(projectDir, gen, queries, tables) {
|
|
1692
1711
|
await super.beforeGenerate(projectDir, gen, queries, tables);
|
|
1693
1712
|
Handlebars.registerHelper("tsType", (column) => {
|
|
@@ -1750,7 +1769,7 @@ function getGenerator(generator) {
|
|
|
1750
1769
|
|
|
1751
1770
|
//#endregion
|
|
1752
1771
|
//#region src/sqltool.ts
|
|
1753
|
-
const GENERATED_FILE_COMMENT = "This file is generated by SQG. Do not edit manually.";
|
|
1772
|
+
const GENERATED_FILE_COMMENT = "This file is generated by SQG (https://sqg.dev). Do not edit manually.";
|
|
1754
1773
|
const configSchema = z.object({ result: z.record(z.string(), z.string()).optional() });
|
|
1755
1774
|
var Config = class Config {
|
|
1756
1775
|
constructor(result) {
|
|
@@ -1894,13 +1913,13 @@ var TableHelper = class {
|
|
|
1894
1913
|
return this.generator.typeMapper;
|
|
1895
1914
|
}
|
|
1896
1915
|
};
|
|
1897
|
-
function generateSourceFile(name, queries, tables, templatePath, generator, config) {
|
|
1916
|
+
function generateSourceFile(name, queries, tables, templatePath, generator, engine, config) {
|
|
1898
1917
|
const templateSrc = readFileSync(templatePath, "utf-8");
|
|
1899
1918
|
const template = Handlebars.compile(templateSrc);
|
|
1900
1919
|
Handlebars.registerHelper("mapType", (column) => generator.mapType(column));
|
|
1901
1920
|
Handlebars.registerHelper("plusOne", (value) => value + 1);
|
|
1902
1921
|
const migrations = queries.filter((q) => q.isMigrate).map((q) => new SqlQueryHelper(q, generator, generator.getStatement(q)));
|
|
1903
|
-
const tableHelpers = tables.filter((t) => !t.skipGenerateFunction).map((t) => new TableHelper(t, generator));
|
|
1922
|
+
const tableHelpers = generator.supportsAppenders(engine) ? tables.filter((t) => !t.skipGenerateFunction).map((t) => new TableHelper(t, generator)) : [];
|
|
1904
1923
|
return template({
|
|
1905
1924
|
generatedComment: GENERATED_FILE_COMMENT,
|
|
1906
1925
|
migrations,
|
|
@@ -1920,7 +1939,7 @@ const ProjectSchema = z.object({
|
|
|
1920
1939
|
version: z.number().describe("Configuration version (currently 1)"),
|
|
1921
1940
|
name: z.string().min(1, "Project name is required").describe("Project name used for generated class names"),
|
|
1922
1941
|
sql: z.array(z.object({
|
|
1923
|
-
engine: z.enum(
|
|
1942
|
+
engine: z.enum(DB_ENGINES).describe(`Database engine: ${DB_ENGINES.join(", ")}`),
|
|
1924
1943
|
files: z.array(z.string().min(1)).min(1, "At least one SQL file is required").describe("SQL files to process"),
|
|
1925
1944
|
gen: z.array(z.object({
|
|
1926
1945
|
generator: z.enum(GENERATOR_NAMES).describe(`Code generator: ${GENERATOR_NAMES.join(", ")}`),
|
|
@@ -1971,7 +1990,7 @@ function parseProjectConfig(filePath) {
|
|
|
1971
1990
|
const obj = parsed;
|
|
1972
1991
|
if (obj.sql && Array.isArray(obj.sql)) for (let i = 0; i < obj.sql.length; i++) {
|
|
1973
1992
|
const sqlConfig = obj.sql[i];
|
|
1974
|
-
if (sqlConfig.engine && !
|
|
1993
|
+
if (sqlConfig.engine && !DB_ENGINES.includes(sqlConfig.engine)) throw new InvalidEngineError(String(sqlConfig.engine), [...DB_ENGINES]);
|
|
1975
1994
|
if (sqlConfig.gen && Array.isArray(sqlConfig.gen)) for (let j = 0; j < sqlConfig.gen.length; j++) {
|
|
1976
1995
|
const genConfig = sqlConfig.gen[j];
|
|
1977
1996
|
if (genConfig.generator && !GENERATOR_NAMES.includes(genConfig.generator)) {
|
|
@@ -2020,11 +2039,11 @@ function validateQueries(queries) {
|
|
|
2020
2039
|
};
|
|
2021
2040
|
}
|
|
2022
2041
|
}
|
|
2023
|
-
async function writeGeneratedFile(projectDir, gen, generator, file, queries, tables
|
|
2042
|
+
async function writeGeneratedFile(projectDir, gen, generator, file, queries, tables, engine) {
|
|
2024
2043
|
await generator.beforeGenerate(projectDir, gen, queries, tables);
|
|
2025
2044
|
const templatePath = join(dirname(new URL(import.meta.url).pathname), gen.template ?? generator.template);
|
|
2026
2045
|
const name = gen.name ?? basename(file, extname(file));
|
|
2027
|
-
const sourceFile = generateSourceFile(name, queries, tables, templatePath, generator, gen.config);
|
|
2046
|
+
const sourceFile = generateSourceFile(name, queries, tables, templatePath, generator, engine, gen.config);
|
|
2028
2047
|
const outputPath = getOutputPath(projectDir, name, gen, generator);
|
|
2029
2048
|
writeFileSync(outputPath, sourceFile);
|
|
2030
2049
|
consola.success(`Generated ${outputPath}`);
|
|
@@ -2136,7 +2155,7 @@ async function processProject(projectPath) {
|
|
|
2136
2155
|
});
|
|
2137
2156
|
}
|
|
2138
2157
|
for (const gen of sql.gen) {
|
|
2139
|
-
const outputPath = await writeGeneratedFile(projectDir, gen, getGenerator(gen.generator), sqlFile, queries, tables);
|
|
2158
|
+
const outputPath = await writeGeneratedFile(projectDir, gen, getGenerator(gen.generator), sqlFile, queries, tables, sql.engine);
|
|
2140
2159
|
files.push(outputPath);
|
|
2141
2160
|
}
|
|
2142
2161
|
}
|
|
@@ -2394,7 +2413,7 @@ sql:
|
|
|
2394
2413
|
async function initProject(options) {
|
|
2395
2414
|
const engine = options.engine || "sqlite";
|
|
2396
2415
|
const output = options.output || "./generated";
|
|
2397
|
-
if (!
|
|
2416
|
+
if (!DB_ENGINES.includes(engine)) throw new InvalidEngineError(engine, [...DB_ENGINES]);
|
|
2398
2417
|
let generator;
|
|
2399
2418
|
if (options.generator) {
|
|
2400
2419
|
if (!(options.generator in SUPPORTED_GENERATORS)) {
|
|
@@ -2436,7 +2455,7 @@ Documentation: https://sqg.dev
|
|
|
2436
2455
|
|
|
2437
2456
|
//#endregion
|
|
2438
2457
|
//#region src/sqg.ts
|
|
2439
|
-
const version = process.env.npm_package_version ?? "0.
|
|
2458
|
+
const version = process.env.npm_package_version ?? "0.4.0";
|
|
2440
2459
|
const description = process.env.npm_package_description ?? "SQG - SQL Query Generator - Type-safe code generation from SQL (https://sqg.dev)";
|
|
2441
2460
|
consola.level = LogLevels.info;
|
|
2442
2461
|
const program = new Command().name("sqg").description(`${description}
|
|
@@ -2486,7 +2505,7 @@ program.argument("<project>", "Path to the project YAML config (sqg.yaml)").hook
|
|
|
2486
2505
|
exit(1);
|
|
2487
2506
|
}
|
|
2488
2507
|
});
|
|
2489
|
-
program.command("init").description("Initialize a new SQG project with example configuration").option("-e, --engine <engine>", `Database engine (${
|
|
2508
|
+
program.command("init").description("Initialize a new SQG project with example configuration").option("-e, --engine <engine>", `Database engine (${DB_ENGINES.join(", ")})`, "sqlite").option("-g, --generator <generator>", `Code generator (${GENERATOR_NAMES.join(", ")})`).option("-o, --output <dir>", "Output directory for generated files", "./generated").option("-f, --force", "Overwrite existing files").action(async (options) => {
|
|
2490
2509
|
const parentOpts = program.opts();
|
|
2491
2510
|
try {
|
|
2492
2511
|
await initProject(options);
|
|
@@ -69,7 +69,7 @@ public class {{className}} {
|
|
|
69
69
|
public record {{rowType}}({{#each columns}}{{type}} {{name}}{{#unless @last}}, {{/unless}}{{/each}}) {}
|
|
70
70
|
{{else}}
|
|
71
71
|
public record {{rowType}}(PreparedStatement statement, RootAllocator allocator, ArrowReader reader,
|
|
72
|
-
{{#each columns}}{{mapType this}} {{name}}{{#unless @last}}, {{/unless}}{{/each}}) implements AutoCloseable {
|
|
72
|
+
{{#each columns}}{{{mapType this}}} {{name}}{{#unless @last}}, {{/unless}}{{/each}}) implements AutoCloseable {
|
|
73
73
|
public boolean loadNextBatch() throws IOException {
|
|
74
74
|
return reader.loadNextBatch();
|
|
75
75
|
}
|
|
@@ -99,7 +99,7 @@ int
|
|
|
99
99
|
{{#*inline "readVectors"}}
|
|
100
100
|
var root = reader.getVectorSchemaRoot();
|
|
101
101
|
|
|
102
|
-
return new {{rowType}}(stmt, allocator, reader, {{#each columns}}({{mapType this}})root.getVector("{{name}}"){{#unless @last}}, {{/unless}}{{/each}});
|
|
102
|
+
return new {{rowType}}(stmt, allocator, reader, {{#each columns}}({{{mapType this}}})root.getVector("{{name}}"){{#unless @last}}, {{/unless}}{{/each}});
|
|
103
103
|
|
|
104
104
|
{{/inline~}}
|
|
105
105
|
|