@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 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 SUPPORTED_ENGINES = [
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 SUPPORTED_ENGINES.map((e) => ` ${e}`).join("\n");
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
- throw new Error(`${nodeName} not found`);
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, nullable } = column;
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
- const mockColumn = {
1479
- name: "",
1480
- type,
1481
- nullable
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(SUPPORTED_ENGINES).describe(`Database engine: ${SUPPORTED_ENGINES.join(", ")}`),
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 && !SUPPORTED_ENGINES.includes(sqlConfig.engine)) throw new InvalidEngineError(String(sqlConfig.engine), [...SUPPORTED_ENGINES]);
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 (!SUPPORTED_ENGINES.includes(engine)) throw new InvalidEngineError(engine, [...SUPPORTED_ENGINES]);
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.3.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 (${SUPPORTED_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) => {
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sqg/sqg",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "SQG - SQL Query Generator - Type-safe code generation from SQL (https://sqg.dev)",
5
5
  "type": "module",
6
6
  "bin": {