@sqg/sqg 0.12.0 → 0.14.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.
@@ -201,6 +201,8 @@ public class {{className}} {
201
201
  }
202
202
  {{/if}}
203
203
 
204
+ {{{declareEnums queries}}}
205
+
204
206
  {{#each queries}}
205
207
  {{#unless skipGenerateFunction}}
206
208
  {{>columnTypesRecord}}
@@ -213,6 +215,7 @@ public class {{className}} {
213
215
  public Stream<{{rowType}}> {{functionName}}Stream({{#each variables}}{{{type}}} {{name}}{{#unless @last}}, {{/unless}}{{/each}}) throws SQLException {
214
216
  var stmt = connection.prepareStatement({{{partsToString sqlQueryParts}}});
215
217
  {{#each parameters}}{{#if isArray}}stmt.setArray({{plusOne @index}}, connection.createArrayOf("{{arrayBaseType}}", {{name}}.toArray()));
218
+ {{else if isEnum}}stmt.setObject({{plusOne @index}}, {{name}}.getValue());
216
219
  {{else}}stmt.setObject({{plusOne @index}}, {{name}});
217
220
  {{/if}}{{/each}}
218
221
  var rs = stmt.executeQuery();
@@ -329,6 +332,7 @@ int
329
332
 
330
333
  {{#*inline "execute"}}
331
334
  {{#each parameters}}{{#if isArray}}stmt.setArray({{plusOne @index}}, connection.createArrayOf("{{arrayBaseType}}", {{name}}.toArray()));
335
+ {{else if isEnum}}stmt.setObject({{plusOne @index}}, {{name}}.getValue());
332
336
  {{else}}stmt.setObject({{plusOne @index}}, {{name}});
333
337
  {{/if}}{{/each}}
334
338
  {{#if isQuery}}
@@ -0,0 +1,256 @@
1
+ # {{generatedComment}}
2
+ from __future__ import annotations
3
+
4
+ {{#if (isDuckDB)}}
5
+ import duckdb
6
+ {{else if (isPostgres)}}
7
+ import psycopg
8
+ {{else}}
9
+ import sqlite3
10
+ {{/if}}
11
+ from dataclasses import dataclass
12
+ from typing import Any
13
+ {{#if (needsDatetime queries)}}
14
+ import datetime
15
+ {{/if}}
16
+ {{#if (needsDecimal queries)}}
17
+ from decimal import Decimal
18
+ {{/if}}
19
+
20
+ {{#each queries}}
21
+ {{#unless skipGenerateFunction}}
22
+ {{#if isQuery}}
23
+ {{#unless isPluck}}
24
+ {{{declareTypes this}}}
25
+
26
+ {{/unless}}
27
+ {{/if}}
28
+ {{/unless}}
29
+ {{/each}}
30
+
31
+ class {{className}}:
32
+ def __init__(self, conn: {{{connType}}}) -> None:
33
+ self._conn = conn
34
+
35
+ @staticmethod
36
+ def get_migrations() -> list[str]:
37
+ return [
38
+ {{#each migrations}}
39
+ {{{quote sqlQuery}}},
40
+ {{/each}}
41
+ ]
42
+
43
+ {{#if config.migrations}}
44
+ @staticmethod
45
+ def apply_migrations(conn: {{{connType}}}, project_name: str = "{{projectName}}") -> None:
46
+ {{#if (isDuckDB)}}
47
+ conn.execute("""
48
+ CREATE TABLE IF NOT EXISTS _sqg_migrations (
49
+ project TEXT NOT NULL,
50
+ migration_id TEXT NOT NULL,
51
+ applied_at TIMESTAMP NOT NULL DEFAULT now(),
52
+ PRIMARY KEY (project, migration_id)
53
+ )""")
54
+ conn.begin()
55
+ try:
56
+ applied = set(
57
+ row[0]
58
+ for row in conn.execute(
59
+ "SELECT migration_id FROM _sqg_migrations WHERE project = $1",
60
+ [project_name],
61
+ ).fetchall()
62
+ )
63
+ migrations: list[tuple[str, str]] = [
64
+ {{#each migrations}}
65
+ ("{{{id}}}", {{{quote sqlQuery}}}),
66
+ {{/each}}
67
+ ]
68
+ for mid, sql in migrations:
69
+ if mid not in applied:
70
+ conn.execute(sql)
71
+ conn.execute(
72
+ "INSERT INTO _sqg_migrations (project, migration_id) VALUES ($1, $2)",
73
+ [project_name, mid],
74
+ )
75
+ conn.commit()
76
+ except Exception:
77
+ conn.rollback()
78
+ raise
79
+ {{else if (isPostgres)}}
80
+ with conn.cursor() as cur:
81
+ cur.execute("""
82
+ CREATE TABLE IF NOT EXISTS _sqg_migrations (
83
+ project TEXT NOT NULL,
84
+ migration_id TEXT NOT NULL,
85
+ applied_at TIMESTAMP NOT NULL DEFAULT now(),
86
+ PRIMARY KEY (project, migration_id)
87
+ )""")
88
+ conn.commit()
89
+ cur.execute(
90
+ "SELECT migration_id FROM _sqg_migrations WHERE project = %s",
91
+ (project_name,),
92
+ )
93
+ applied = set(row[0] for row in cur.fetchall())
94
+ migrations: list[tuple[str, str]] = [
95
+ {{#each migrations}}
96
+ ("{{{id}}}", {{{quote sqlQuery}}}),
97
+ {{/each}}
98
+ ]
99
+ for mid, sql in migrations:
100
+ if mid not in applied:
101
+ cur.execute(sql)
102
+ cur.execute(
103
+ "INSERT INTO _sqg_migrations (project, migration_id) VALUES (%s, %s)",
104
+ (project_name, mid),
105
+ )
106
+ conn.commit()
107
+ {{else}}
108
+ conn.execute("""
109
+ CREATE TABLE IF NOT EXISTS _sqg_migrations (
110
+ project TEXT NOT NULL,
111
+ migration_id TEXT NOT NULL,
112
+ applied_at TEXT NOT NULL DEFAULT (datetime('now')),
113
+ PRIMARY KEY (project, migration_id)
114
+ )""")
115
+ applied = set(
116
+ row[0]
117
+ for row in conn.execute(
118
+ "SELECT migration_id FROM _sqg_migrations WHERE project = ?",
119
+ (project_name,),
120
+ ).fetchall()
121
+ )
122
+ migrations: list[tuple[str, str]] = [
123
+ {{#each migrations}}
124
+ ("{{{id}}}", {{{quote sqlQuery}}}),
125
+ {{/each}}
126
+ ]
127
+ for mid, sql in migrations:
128
+ if mid not in applied:
129
+ conn.executescript(sql)
130
+ conn.execute(
131
+ "INSERT INTO _sqg_migrations (project, migration_id) VALUES (?, ?)",
132
+ (project_name, mid),
133
+ )
134
+ conn.commit()
135
+ {{/if}}
136
+ {{/if}}
137
+
138
+ {{#each queries}}
139
+ {{#unless skipGenerateFunction}}
140
+ {{#if isQuery}}
141
+ {{#if isPluck}}
142
+ {{#if isOne}}
143
+ def {{functionName}}(self{{#each variables}}, {{name}}: {{{type}}}{{/each}}) -> {{{pyTypeOrNone (lookup columns 0)}}}:
144
+ row = self._conn.execute(
145
+ {{{quote sqlQuery}}}, {{> params}}
146
+ ).fetchone()
147
+ if row is None:
148
+ return None
149
+ return row[0]
150
+
151
+ def {{functionName}}_raw(self{{#each variables}}, {{name}}: {{{type}}}{{/each}}) -> tuple[Any, ...] | None:
152
+ return self._conn.execute(
153
+ {{{quote sqlQuery}}}, {{> params}}
154
+ ).fetchone()
155
+
156
+ {{else}}
157
+ def {{functionName}}(self{{#each variables}}, {{name}}: {{{type}}}{{/each}}) -> list[{{{pyType (lookup columns 0)}}}]:
158
+ rows = self._conn.execute(
159
+ {{{quote sqlQuery}}}, {{> params}}
160
+ ).fetchall()
161
+ return [r[0] for r in rows]
162
+
163
+ def {{functionName}}_raw(self{{#each variables}}, {{name}}: {{{type}}}{{/each}}) -> list[tuple[Any, ...]]:
164
+ return self._conn.execute(
165
+ {{{quote sqlQuery}}}, {{> params}}
166
+ ).fetchall()
167
+
168
+ {{/if}}
169
+ {{else}}
170
+ {{#if isOne}}
171
+ def {{functionName}}(self{{#each variables}}, {{name}}: {{{type}}}{{/each}}) -> {{{rowType}}} | None:
172
+ row = self._conn.execute(
173
+ {{{quote sqlQuery}}}, {{> params}}
174
+ ).fetchone()
175
+ if row is None:
176
+ return None
177
+ return {{{constructRow this}}}
178
+
179
+ def {{functionName}}_raw(self{{#each variables}}, {{name}}: {{{type}}}{{/each}}) -> tuple[Any, ...] | None:
180
+ return self._conn.execute(
181
+ {{{quote sqlQuery}}}, {{> params}}
182
+ ).fetchone()
183
+
184
+ {{else}}
185
+ def {{functionName}}(self{{#each variables}}, {{name}}: {{{type}}}{{/each}}) -> list[{{{rowType}}}]:
186
+ rows = self._conn.execute(
187
+ {{{quote sqlQuery}}}, {{> params}}
188
+ ).fetchall()
189
+ return [{{{constructRow this}}} for row in rows]
190
+
191
+ def {{functionName}}_raw(self{{#each variables}}, {{name}}: {{{type}}}{{/each}}) -> list[tuple[Any, ...]]:
192
+ return self._conn.execute(
193
+ {{{quote sqlQuery}}}, {{> params}}
194
+ ).fetchall()
195
+
196
+ {{/if}}
197
+ {{/if}}
198
+ {{else}}
199
+ def {{functionName}}(self{{#each variables}}, {{name}}: {{{type}}}{{/each}}) -> None:
200
+ self._conn.execute(
201
+ {{{quote sqlQuery}}}, {{> params}}
202
+ )
203
+
204
+ {{/if}}
205
+ {{/unless}}
206
+ {{/each}}
207
+ {{#if (isDuckDB)}}
208
+ {{#if tables.length}}
209
+ # ==================== Appenders ====================
210
+ {{#each tables}}
211
+
212
+ def {{functionName}}(self) -> {{className}}:
213
+ return {{className}}(self._conn.cursor())
214
+
215
+ {{/each}}
216
+ {{/if}}
217
+ {{/if}}
218
+
219
+ {{#if (isDuckDB)}}
220
+ {{#each tables}}
221
+
222
+ @dataclass(frozen=True)
223
+ class {{rowTypeName}}:
224
+ {{#each columns}}
225
+ {{pyVarName name}}: {{{pyType this}}}
226
+ {{/each}}
227
+
228
+
229
+ class {{className}}:
230
+ def __init__(self, cursor: duckdb.DuckDBPyConnection) -> None:
231
+ self._cursor = cursor
232
+
233
+ def append(self, row: {{rowTypeName}}) -> {{className}}:
234
+ self._cursor.execute(
235
+ "INSERT INTO {{tableName}} VALUES ({{> placeholders}})",
236
+ [{{#each columns}}row.{{pyVarName name}}{{#unless @last}}, {{/unless}}{{/each}}],
237
+ )
238
+ return self
239
+
240
+ def append_many(self, rows: list[{{rowTypeName}}]) -> {{className}}:
241
+ for row in rows:
242
+ self.append(row)
243
+ return self
244
+
245
+ def close(self) -> None:
246
+ self._cursor.close()
247
+
248
+ {{/each}}
249
+ {{/if}}
250
+
251
+ {{!-- ==================== Inline partials ==================== --}}
252
+
253
+ {{#*inline "params"}}{{#if (isDuckDB)}}[{{#each parameterNames}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}]{{else}}({{#each parameterNames}}{{this}}, {{/each}}){{/if}}{{/inline}}
254
+
255
+
256
+ {{#*inline "placeholders"}}{{#each columns}}${{plusOne @index}}{{#unless @last}}, {{/unless}}{{/each}}{{/inline}}
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@sqg/sqg",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "SQG - SQL Query Generator - Type-safe code generation from SQL (https://sqg.dev)",
5
5
  "type": "module",
6
6
  "bin": {
7
- "sqg": "./dist/sqg.mjs"
7
+ "sqg": "dist/sqg.mjs"
8
8
  },
9
9
  "files": [
10
10
  "dist"
@@ -20,6 +20,19 @@
20
20
  "bugs": {
21
21
  "url": "https://github.com/sqg-dev/sqg/issues"
22
22
  },
23
+ "scripts": {
24
+ "prepublishOnly": "pnpm run build",
25
+ "build": "tsc --noEmit && tsdown && cp -r src/templates dist",
26
+ "lezer-gen": "lezer-generator src/parser/sql.grammar -o src/parser/sql-parser.ts",
27
+ "test-grammar": "tsx src/test-grammar.ts",
28
+ "check": "biome check --write src/",
29
+ "check:errors": "pnpm biome check --write --diagnostic-level=error src/",
30
+ "test": "vitest",
31
+ "coverage": "vitest run --coverage",
32
+ "test:ui": "vitest --ui",
33
+ "test:run": "vitest run",
34
+ "sqg": "tsx src/sqg.ts"
35
+ },
23
36
  "keywords": [
24
37
  "sql",
25
38
  "codegen",
@@ -37,53 +50,45 @@
37
50
  ],
38
51
  "author": "Uwe Maurer",
39
52
  "license": "Apache-2.0",
53
+ "packageManager": "pnpm@10.26.0",
40
54
  "dependencies": {
41
- "@biomejs/biome": "2.3.3",
42
- "@duckdb/node-api": "1.4.3-r.2",
43
- "@duckdb/node-bindings-linux-x64": "1.4.3-r.2",
44
- "@lezer-unofficial/printer": "^1.0.1",
45
- "@lezer/common": "^1.5.0",
46
- "@lezer/generator": "^1.8.0",
47
- "@lezer/lr": "^1.4.6",
48
- "@modelcontextprotocol/sdk": "^1.0.4",
55
+ "@biomejs/biome": "catalog:",
56
+ "@clack/prompts": "^1.1.0",
57
+ "@duckdb/node-api": "catalog:",
58
+ "@duckdb/node-bindings-linux-x64": "catalog:",
59
+ "@lezer-unofficial/printer": "catalog:",
60
+ "@lezer/common": "catalog:",
61
+ "@lezer/generator": "catalog:",
62
+ "@lezer/lr": "catalog:",
63
+ "@modelcontextprotocol/sdk": "catalog:",
49
64
  "@testcontainers/postgresql": "^10.21.0",
50
- "better-sqlite3": "^12.5.0",
51
- "commander": "^14.0.2",
52
- "consola": "^3.4.2",
53
- "dotenv": "^17.2.3",
54
- "es-toolkit": "^1.43.0",
55
- "handlebars": "^4.7.8",
56
- "pg": "^8.16.3",
57
- "pg-types": "^4.1.0",
58
- "prettier": "^3.7.4",
59
- "prettier-plugin-java": "^2.7.7",
65
+ "better-sqlite3": "catalog:",
66
+ "commander": "catalog:",
67
+ "consola": "catalog:",
68
+ "dotenv": "catalog:",
69
+ "es-toolkit": "catalog:",
70
+ "handlebars": "catalog:",
71
+ "pg": "catalog:",
72
+ "pg-types": "catalog:",
73
+ "picocolors": "^1.1.1",
74
+ "prettier": "catalog:",
75
+ "prettier-plugin-java": "catalog:",
60
76
  "update-notifier": "^7.3.1",
61
- "yaml": "^2.8.2",
62
- "zod": "^4.3.5"
77
+ "yaml": "catalog:",
78
+ "yocto-spinner": "^1.1.0",
79
+ "zod": "catalog:"
63
80
  },
64
81
  "devDependencies": {
65
82
  "@libsql/client": "^0.17.0",
66
83
  "@tursodatabase/database": "^0.4.3",
67
- "@types/better-sqlite3": "^7.6.13",
68
- "@types/node": "^25.0.3",
69
- "@types/pg": "^8.16.0",
84
+ "@types/better-sqlite3": "catalog:",
85
+ "@types/node": "catalog:",
86
+ "@types/pg": "catalog:",
70
87
  "@types/update-notifier": "^6.0.8",
71
- "@vitest/ui": "^4.0.16",
72
- "tsdown": "0.18.0",
73
- "tsx": "^4.21.0",
74
- "typescript": "^5.9.3",
75
- "vitest": "^4.0.16"
76
- },
77
- "scripts": {
78
- "build": "tsc --noEmit && tsdown && cp -r src/templates dist",
79
- "lezer-gen": "lezer-generator src/parser/sql.grammar -o src/parser/sql-parser.ts",
80
- "test-grammar": "tsx src/test-grammar.ts",
81
- "check": "biome check --write src/",
82
- "check:errors": "pnpm biome check --write --diagnostic-level=error src/",
83
- "test": "vitest",
84
- "coverage": "vitest run --coverage",
85
- "test:ui": "vitest --ui",
86
- "test:run": "vitest run",
87
- "sqg": "tsx src/sqg.ts"
88
+ "@vitest/ui": "catalog:",
89
+ "tsdown": "catalog:",
90
+ "tsx": "catalog:",
91
+ "typescript": "catalog:",
92
+ "vitest": "catalog:"
88
93
  }
89
- }
94
+ }