@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.
- package/README.md +8 -6
- package/dist/sqg.mjs +793 -117
- package/dist/templates/java-jdbc.hbs +4 -0
- package/dist/templates/python.hbs +256 -0
- package/package.json +48 -43
|
@@ -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.
|
|
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": "
|
|
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": "
|
|
42
|
-
"@
|
|
43
|
-
"@duckdb/node-
|
|
44
|
-
"@
|
|
45
|
-
"@lezer/
|
|
46
|
-
"@lezer/
|
|
47
|
-
"@lezer/
|
|
48
|
-
"@
|
|
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": "
|
|
51
|
-
"commander": "
|
|
52
|
-
"consola": "
|
|
53
|
-
"dotenv": "
|
|
54
|
-
"es-toolkit": "
|
|
55
|
-
"handlebars": "
|
|
56
|
-
"pg": "
|
|
57
|
-
"pg-types": "
|
|
58
|
-
"
|
|
59
|
-
"prettier
|
|
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": "
|
|
62
|
-
"
|
|
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": "
|
|
68
|
-
"@types/node": "
|
|
69
|
-
"@types/pg": "
|
|
84
|
+
"@types/better-sqlite3": "catalog:",
|
|
85
|
+
"@types/node": "catalog:",
|
|
86
|
+
"@types/pg": "catalog:",
|
|
70
87
|
"@types/update-notifier": "^6.0.8",
|
|
71
|
-
"@vitest/ui": "
|
|
72
|
-
"tsdown": "
|
|
73
|
-
"tsx": "
|
|
74
|
-
"typescript": "
|
|
75
|
-
"vitest": "
|
|
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
|
+
}
|