frg-data-diff 2.0.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.
Files changed (62) hide show
  1. package/README.md +628 -0
  2. package/dist/apply/apply-diff.d.ts +24 -0
  3. package/dist/apply/apply-diff.js +205 -0
  4. package/dist/apply/conflict.d.ts +20 -0
  5. package/dist/apply/conflict.js +14 -0
  6. package/dist/apply/plan.d.ts +13 -0
  7. package/dist/apply/plan.js +37 -0
  8. package/dist/cli/apply.d.ts +8 -0
  9. package/dist/cli/apply.js +270 -0
  10. package/dist/cli/generator.d.ts +9 -0
  11. package/dist/cli/generator.js +804 -0
  12. package/dist/cli/pg-triggers.d.ts +2 -0
  13. package/dist/cli/pg-triggers.js +185 -0
  14. package/dist/cli/root.d.ts +8 -0
  15. package/dist/cli/root.js +231 -0
  16. package/dist/cli/sql.d.ts +9 -0
  17. package/dist/cli/sql.js +158 -0
  18. package/dist/config/config-schema.d.ts +380 -0
  19. package/dist/config/config-schema.js +95 -0
  20. package/dist/config/load-config.d.ts +12 -0
  21. package/dist/config/load-config.js +87 -0
  22. package/dist/config/resolve-options.d.ts +95 -0
  23. package/dist/config/resolve-options.js +183 -0
  24. package/dist/config/write-config.d.ts +18 -0
  25. package/dist/config/write-config.js +103 -0
  26. package/dist/db/connection.d.ts +28 -0
  27. package/dist/db/connection.js +34 -0
  28. package/dist/db/metadata.d.ts +70 -0
  29. package/dist/db/metadata.js +238 -0
  30. package/dist/db/pg-triggers.d.ts +27 -0
  31. package/dist/db/pg-triggers.js +59 -0
  32. package/dist/db/sql.d.ts +72 -0
  33. package/dist/db/sql.js +250 -0
  34. package/dist/diff/diff-schema.d.ts +380 -0
  35. package/dist/diff/diff-schema.js +67 -0
  36. package/dist/diff/generate-diff.d.ts +20 -0
  37. package/dist/diff/generate-diff.js +224 -0
  38. package/dist/diff/pg-triggers-diff.d.ts +11 -0
  39. package/dist/diff/pg-triggers-diff.js +161 -0
  40. package/dist/diff/serialize-value.d.ts +35 -0
  41. package/dist/diff/serialize-value.js +242 -0
  42. package/dist/diff/write-diff-yaml.d.ts +3 -0
  43. package/dist/diff/write-diff-yaml.js +48 -0
  44. package/dist/schema-diff/generate-schema-diff.d.ts +12 -0
  45. package/dist/schema-diff/generate-schema-diff.js +355 -0
  46. package/dist/schema-diff/generate-schema-sql.d.ts +20 -0
  47. package/dist/schema-diff/generate-schema-sql.js +187 -0
  48. package/dist/schema-diff/schema-diff-schema.d.ts +1088 -0
  49. package/dist/schema-diff/schema-diff-schema.js +70 -0
  50. package/dist/shared/env-values.d.ts +11 -0
  51. package/dist/shared/env-values.js +100 -0
  52. package/dist/shared/generator-wizard.d.ts +10 -0
  53. package/dist/shared/generator-wizard.js +337 -0
  54. package/dist/shared/identifiers.d.ts +24 -0
  55. package/dist/shared/identifiers.js +45 -0
  56. package/dist/shared/prompts.d.ts +26 -0
  57. package/dist/shared/prompts.js +104 -0
  58. package/dist/shared/summary.d.ts +33 -0
  59. package/dist/shared/summary.js +41 -0
  60. package/dist/sql/generate-sql.d.ts +19 -0
  61. package/dist/sql/generate-sql.js +223 -0
  62. package/package.json +39 -0
@@ -0,0 +1,27 @@
1
+ import { type PoolClient } from "pg";
2
+ export interface TriggerInfo {
3
+ triggerName: string;
4
+ triggerDefinition: string;
5
+ }
6
+ export interface FunctionInfo {
7
+ functionSchema: string;
8
+ functionName: string;
9
+ functionDefinition: string;
10
+ }
11
+ export interface TableTriggersAndFunctions {
12
+ table: string;
13
+ triggers: TriggerInfo[];
14
+ functions: FunctionInfo[];
15
+ }
16
+ export interface FetchTriggersAndFunctionsForTablesOptions {
17
+ onTableStart?: (table: string, tablePosition: number, totalTables: number) => void;
18
+ }
19
+ /**
20
+ * Fetches user-defined triggers and their associated functions for a given table.
21
+ */
22
+ export declare function fetchTriggersAndFunctions(client: PoolClient, schema: string, table: string): Promise<TableTriggersAndFunctions>;
23
+ /**
24
+ * Fetches triggers and functions for a list of tables.
25
+ */
26
+ export declare function fetchTriggersAndFunctionsForTables(client: PoolClient, schema: string, tables: string[], options?: FetchTriggersAndFunctionsForTablesOptions): Promise<TableTriggersAndFunctions[]>;
27
+ //# sourceMappingURL=pg-triggers.d.ts.map
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fetchTriggersAndFunctions = fetchTriggersAndFunctions;
4
+ exports.fetchTriggersAndFunctionsForTables = fetchTriggersAndFunctionsForTables;
5
+ /**
6
+ * Fetches user-defined triggers and their associated functions for a given table.
7
+ */
8
+ async function fetchTriggersAndFunctions(client, schema, table) {
9
+ const result = await client.query(`
10
+ SELECT
11
+ trg.tgname AS trigger_name,
12
+ pg_get_triggerdef(trg.oid) AS trigger_definition,
13
+ proc_ns.nspname AS function_schema,
14
+ proc.proname AS function_name,
15
+ pg_get_functiondef(proc.oid) AS function_definition
16
+ FROM pg_trigger trg
17
+ JOIN pg_class tbl ON tbl.oid = trg.tgrelid
18
+ JOIN pg_namespace ns ON ns.oid = tbl.relnamespace
19
+ JOIN pg_proc proc ON proc.oid = trg.tgfoid
20
+ JOIN pg_namespace proc_ns ON proc_ns.oid = proc.pronamespace
21
+ WHERE ns.nspname = $1
22
+ AND tbl.relname = $2
23
+ AND trg.tgisinternal = false
24
+ ORDER BY trg.tgname
25
+ `, [schema, table]);
26
+ const triggers = [];
27
+ const functions = [];
28
+ const seenFunctions = new Set();
29
+ for (const row of result.rows) {
30
+ triggers.push({
31
+ triggerName: row.trigger_name,
32
+ triggerDefinition: row.trigger_definition,
33
+ });
34
+ const funcKey = `${row.function_schema}.${row.function_name}`;
35
+ if (!seenFunctions.has(funcKey)) {
36
+ seenFunctions.add(funcKey);
37
+ functions.push({
38
+ functionSchema: row.function_schema,
39
+ functionName: row.function_name,
40
+ functionDefinition: row.function_definition,
41
+ });
42
+ }
43
+ }
44
+ return { table, triggers, functions };
45
+ }
46
+ /**
47
+ * Fetches triggers and functions for a list of tables.
48
+ */
49
+ async function fetchTriggersAndFunctionsForTables(client, schema, tables, options = {}) {
50
+ const results = [];
51
+ for (const [tableIndex, table] of tables.entries()) {
52
+ if (options.onTableStart) {
53
+ options.onTableStart(table, tableIndex + 1, tables.length);
54
+ }
55
+ results.push(await fetchTriggersAndFunctions(client, schema, table));
56
+ }
57
+ return results;
58
+ }
59
+ //# sourceMappingURL=pg-triggers.js.map
@@ -0,0 +1,72 @@
1
+ import { type PoolClient } from "pg";
2
+ /**
3
+ * Builds a parameterized WHERE clause for primary key equality.
4
+ * Returns the clause and the parameter values.
5
+ *
6
+ * Example: pk = ['id', 'tenant_id'], pkValues = {id: 1, tenant_id: 2}
7
+ * Result: `"id" = $1 AND "tenant_id" = $2`, [1, 2]
8
+ */
9
+ export declare function buildPkWhereClause(pkColumns: string[], pkValues: Record<string, unknown>, startIndex?: number): {
10
+ clause: string;
11
+ params: unknown[];
12
+ };
13
+ /**
14
+ * Builds a parameterized WHERE clause using IS NOT DISTINCT FROM semantics
15
+ * for NULL-safe comparison.
16
+ *
17
+ * When a column type map is provided:
18
+ * - geometric types (point, circle, etc.): skipped — PostgreSQL has no equality operator for them.
19
+ * The PK check still ensures the correct row is targeted.
20
+ * - json/jsonb columns: both sides are cast to jsonb for structural comparison
21
+ * - interval and range types: parameter gets an explicit type cast
22
+ *
23
+ * @param columns - Column names to include in the WHERE clause
24
+ * @param values - Deserialized values for each column
25
+ * @param startIndex - Paramter index offset (default 1)
26
+ * @param columnTypes - Optional map of column name -> PostgreSQL data_type
27
+ */
28
+ export declare function buildNullSafeWhereClause(columns: string[], values: Record<string, unknown>, startIndex?: number, columnTypes?: Record<string, string>): {
29
+ clause: string;
30
+ params: unknown[];
31
+ };
32
+ /**
33
+ * Fetches all rows from a table ordered by primary key, in batches.
34
+ * Returns rows as an array of plain objects.
35
+ */
36
+ export declare function fetchRowsBatched(client: PoolClient, schema: string, table: string, columns: string[], pkColumns: string[], batchSize?: number, whereDataFilter?: string, onBatchProgress?: (progress: FetchRowsBatchProgress) => void): Promise<Record<string, unknown>[]>;
37
+ export interface FetchRowsBatchProgress {
38
+ batchNumber: number;
39
+ batchRows: number;
40
+ fetchedRows: number;
41
+ }
42
+ export declare function buildSelectRowsQuery(options: {
43
+ schema: string;
44
+ table: string;
45
+ columns: string[];
46
+ pkColumns: string[];
47
+ limitParam: number;
48
+ offsetParam: number;
49
+ whereDataFilter?: string;
50
+ }): string;
51
+ /**
52
+ * Executes an INSERT statement for a single row.
53
+ * Uses parameterized queries — never string-concatenates values.
54
+ */
55
+ export declare function insertRow(client: PoolClient, schema: string, table: string, row: Record<string, unknown>, columns: string[]): Promise<void>;
56
+ /**
57
+ * Executes an UPSERT (INSERT ... ON CONFLICT DO UPDATE) for a single row.
58
+ */
59
+ export declare function upsertRow(client: PoolClient, schema: string, table: string, row: Record<string, unknown>, columns: string[], pkColumns: string[]): Promise<void>;
60
+ /**
61
+ * Executes an UPDATE statement for a single row with guarded column checks.
62
+ * Uses IS NOT DISTINCT FROM for NULL-safe comparisons.
63
+ *
64
+ * Returns the number of rows actually updated (0 or 1).
65
+ */
66
+ export declare function updateRow(client: PoolClient, schema: string, table: string, pkColumns: string[], pkValues: Record<string, unknown>, setColumns: string[], setValues: Record<string, unknown>, guardColumns: string[], guardValues: Record<string, unknown>, columnTypes?: Record<string, string>): Promise<number>;
67
+ /**
68
+ * Executes a DELETE statement for a single row with guard checks.
69
+ * Returns the number of rows deleted (0 or 1).
70
+ */
71
+ export declare function deleteRow(client: PoolClient, schema: string, table: string, pkColumns: string[], pkValues: Record<string, unknown>, guardColumns: string[], guardValues: Record<string, unknown>, columnTypes?: Record<string, string>): Promise<number>;
72
+ //# sourceMappingURL=sql.d.ts.map
package/dist/db/sql.js ADDED
@@ -0,0 +1,250 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildPkWhereClause = buildPkWhereClause;
4
+ exports.buildNullSafeWhereClause = buildNullSafeWhereClause;
5
+ exports.fetchRowsBatched = fetchRowsBatched;
6
+ exports.buildSelectRowsQuery = buildSelectRowsQuery;
7
+ exports.insertRow = insertRow;
8
+ exports.upsertRow = upsertRow;
9
+ exports.updateRow = updateRow;
10
+ exports.deleteRow = deleteRow;
11
+ const identifiers_1 = require("../shared/identifiers");
12
+ /**
13
+ * Builds a parameterized WHERE clause for primary key equality.
14
+ * Returns the clause and the parameter values.
15
+ *
16
+ * Example: pk = ['id', 'tenant_id'], pkValues = {id: 1, tenant_id: 2}
17
+ * Result: `"id" = $1 AND "tenant_id" = $2`, [1, 2]
18
+ */
19
+ function buildPkWhereClause(pkColumns, pkValues, startIndex = 1) {
20
+ const parts = [];
21
+ const params = [];
22
+ for (let i = 0; i < pkColumns.length; i++) {
23
+ const col = pkColumns[i];
24
+ parts.push(`${(0, identifiers_1.quoteIdentifier)(col)} = $${startIndex + i}`);
25
+ params.push(pkValues[col]);
26
+ }
27
+ return { clause: parts.join(" AND "), params };
28
+ }
29
+ /**
30
+ * PostgreSQL types that have no equality operator and cannot be used in IS NOT DISTINCT FROM.
31
+ * These columns are skipped from guard WHERE clauses.
32
+ * The primary key check is still present to ensure the correct row is targeted.
33
+ */
34
+ const UNGUARDABLE_TYPES = new Set([
35
+ "point",
36
+ "line",
37
+ "lseg",
38
+ "box",
39
+ "path",
40
+ "polygon",
41
+ "circle",
42
+ ]);
43
+ /**
44
+ * Geometric and special PostgreSQL data types that need an explicit cast in parameterized queries
45
+ * when the value is passed as a string.
46
+ */
47
+ const TYPES_NEEDING_CAST = {
48
+ // Range types (pg returns as strings, need explicit cast for unambiguous type resolution)
49
+ int4range: "int4range",
50
+ int8range: "int8range",
51
+ numrange: "numrange",
52
+ tsrange: "tsrange",
53
+ tstzrange: "tstzrange",
54
+ daterange: "daterange",
55
+ // Interval type
56
+ interval: "interval",
57
+ };
58
+ /**
59
+ * Builds a parameterized WHERE clause using IS NOT DISTINCT FROM semantics
60
+ * for NULL-safe comparison.
61
+ *
62
+ * When a column type map is provided:
63
+ * - geometric types (point, circle, etc.): skipped — PostgreSQL has no equality operator for them.
64
+ * The PK check still ensures the correct row is targeted.
65
+ * - json/jsonb columns: both sides are cast to jsonb for structural comparison
66
+ * - interval and range types: parameter gets an explicit type cast
67
+ *
68
+ * @param columns - Column names to include in the WHERE clause
69
+ * @param values - Deserialized values for each column
70
+ * @param startIndex - Paramter index offset (default 1)
71
+ * @param columnTypes - Optional map of column name -> PostgreSQL data_type
72
+ */
73
+ function buildNullSafeWhereClause(columns, values, startIndex = 1, columnTypes) {
74
+ const parts = [];
75
+ const params = [];
76
+ let paramIndex = startIndex;
77
+ for (const col of columns) {
78
+ const val = values[col];
79
+ const dataType = columnTypes?.[col];
80
+ // Skip columns whose types don't support equality (e.g., geometric types)
81
+ if (dataType && UNGUARDABLE_TYPES.has(dataType)) {
82
+ continue;
83
+ }
84
+ if (dataType === "json" || dataType === "jsonb") {
85
+ if (val === null || val === undefined) {
86
+ parts.push(`${(0, identifiers_1.quoteIdentifier)(col)} IS NOT DISTINCT FROM $${paramIndex}`);
87
+ params.push(null);
88
+ }
89
+ else {
90
+ // Cast both sides to jsonb for structural comparison (handles key ordering)
91
+ // and avoids "operator does not exist: json = unknown"
92
+ parts.push(`${(0, identifiers_1.quoteIdentifier)(col)}::jsonb IS NOT DISTINCT FROM $${paramIndex}::jsonb`);
93
+ params.push(typeof val === "object" ? JSON.stringify(val) : String(val));
94
+ }
95
+ }
96
+ else if (dataType && TYPES_NEEDING_CAST[dataType] !== undefined) {
97
+ // Interval and range types: add explicit cast for unambiguous type resolution
98
+ const castType = TYPES_NEEDING_CAST[dataType];
99
+ if (val === null || val === undefined) {
100
+ parts.push(`${(0, identifiers_1.quoteIdentifier)(col)} IS NOT DISTINCT FROM $${paramIndex}`);
101
+ params.push(null);
102
+ }
103
+ else {
104
+ parts.push(`${(0, identifiers_1.quoteIdentifier)(col)} IS NOT DISTINCT FROM $${paramIndex}::${castType}`);
105
+ params.push(val);
106
+ }
107
+ }
108
+ else if (val !== null &&
109
+ val !== undefined &&
110
+ typeof val === "object" &&
111
+ !Buffer.isBuffer(val) &&
112
+ !Array.isArray(val)) {
113
+ // Fallback for unknown object types without type map: use jsonb cast
114
+ parts.push(`${(0, identifiers_1.quoteIdentifier)(col)}::jsonb IS NOT DISTINCT FROM $${paramIndex}::jsonb`);
115
+ params.push(JSON.stringify(val));
116
+ }
117
+ else {
118
+ parts.push(`${(0, identifiers_1.quoteIdentifier)(col)} IS NOT DISTINCT FROM $${paramIndex}`);
119
+ params.push(val);
120
+ }
121
+ paramIndex++;
122
+ }
123
+ return { clause: parts.join(" AND "), params };
124
+ }
125
+ /**
126
+ * Fetches all rows from a table ordered by primary key, in batches.
127
+ * Returns rows as an array of plain objects.
128
+ */
129
+ async function fetchRowsBatched(client, schema, table, columns, pkColumns, batchSize = 1000, whereDataFilter, onBatchProgress) {
130
+ const rows = [];
131
+ let offset = 0;
132
+ let batchNumber = 0;
133
+ while (true) {
134
+ batchNumber++;
135
+ const query = buildSelectRowsQuery({
136
+ schema,
137
+ table,
138
+ columns,
139
+ pkColumns,
140
+ limitParam: 1,
141
+ offsetParam: 2,
142
+ whereDataFilter,
143
+ });
144
+ const result = await client.query(query, [
145
+ batchSize,
146
+ offset,
147
+ ]);
148
+ rows.push(...result.rows);
149
+ if (onBatchProgress) {
150
+ onBatchProgress({
151
+ batchNumber,
152
+ batchRows: result.rows.length,
153
+ fetchedRows: rows.length,
154
+ });
155
+ }
156
+ if (result.rows.length < batchSize) {
157
+ break;
158
+ }
159
+ offset += batchSize;
160
+ }
161
+ return rows;
162
+ }
163
+ function buildSelectRowsQuery(options) {
164
+ const qualifiedTable = (0, identifiers_1.quoteQualifiedTable)(options.schema, options.table);
165
+ const quotedColumns = options.columns.map(identifiers_1.quoteIdentifier).join(", ");
166
+ const orderBy = options.pkColumns.map(identifiers_1.quoteIdentifier).join(", ");
167
+ const whereClause = options.whereDataFilter !== undefined
168
+ ? ` WHERE (${options.whereDataFilter})`
169
+ : "";
170
+ return `SELECT ${quotedColumns} FROM ${qualifiedTable}${whereClause} ORDER BY ${orderBy} LIMIT $${options.limitParam} OFFSET $${options.offsetParam}`;
171
+ }
172
+ /**
173
+ * Executes an INSERT statement for a single row.
174
+ * Uses parameterized queries — never string-concatenates values.
175
+ */
176
+ async function insertRow(client, schema, table, row, columns) {
177
+ const qualifiedTable = (0, identifiers_1.quoteQualifiedTable)(schema, table);
178
+ const quotedCols = columns.map(identifiers_1.quoteIdentifier).join(", ");
179
+ const params = columns.map((c) => row[c]);
180
+ const placeholders = columns.map((_, i) => `$${i + 1}`).join(", ");
181
+ await client.query(`INSERT INTO ${qualifiedTable} (${quotedCols}) VALUES (${placeholders})`, params);
182
+ }
183
+ /**
184
+ * Executes an UPSERT (INSERT ... ON CONFLICT DO UPDATE) for a single row.
185
+ */
186
+ async function upsertRow(client, schema, table, row, columns, pkColumns) {
187
+ const qualifiedTable = (0, identifiers_1.quoteQualifiedTable)(schema, table);
188
+ const quotedCols = columns.map(identifiers_1.quoteIdentifier).join(", ");
189
+ const params = columns.map((c) => row[c]);
190
+ const placeholders = columns.map((_, i) => `$${i + 1}`).join(", ");
191
+ const conflictCols = pkColumns.map(identifiers_1.quoteIdentifier).join(", ");
192
+ const updateCols = columns
193
+ .filter((c) => !pkColumns.includes(c))
194
+ .map((c, i) => {
195
+ const paramIndex = columns.indexOf(c) + 1;
196
+ return `${(0, identifiers_1.quoteIdentifier)(c)} = $${paramIndex}`;
197
+ })
198
+ .join(", ");
199
+ if (updateCols === "") {
200
+ // Only PK columns — just do a plain insert that ignores conflicts
201
+ await client.query(`INSERT INTO ${qualifiedTable} (${quotedCols}) VALUES (${placeholders}) ON CONFLICT (${conflictCols}) DO NOTHING`, params);
202
+ }
203
+ else {
204
+ await client.query(`INSERT INTO ${qualifiedTable} (${quotedCols}) VALUES (${placeholders}) ON CONFLICT (${conflictCols}) DO UPDATE SET ${updateCols}`, params);
205
+ }
206
+ }
207
+ /**
208
+ * Executes an UPDATE statement for a single row with guarded column checks.
209
+ * Uses IS NOT DISTINCT FROM for NULL-safe comparisons.
210
+ *
211
+ * Returns the number of rows actually updated (0 or 1).
212
+ */
213
+ async function updateRow(client, schema, table, pkColumns, pkValues, setColumns, setValues, guardColumns, guardValues, columnTypes) {
214
+ const qualifiedTable = (0, identifiers_1.quoteQualifiedTable)(schema, table);
215
+ const setParams = [];
216
+ const setClauses = setColumns.map((col, i) => {
217
+ setParams.push(setValues[col]);
218
+ return `${(0, identifiers_1.quoteIdentifier)(col)} = $${i + 1}`;
219
+ });
220
+ const guardStartIndex = setParams.length + 1;
221
+ const pkWhere = buildPkWhereClause(pkColumns, pkValues, guardStartIndex);
222
+ const allParams = [...setParams, ...pkWhere.params];
223
+ let whereClause = pkWhere.clause;
224
+ const guardStartIndex2 = allParams.length + 1;
225
+ if (guardColumns.length > 0) {
226
+ const guardWhere = buildNullSafeWhereClause(guardColumns, guardValues, guardStartIndex2, columnTypes);
227
+ allParams.push(...guardWhere.params);
228
+ whereClause += " AND " + guardWhere.clause;
229
+ }
230
+ const result = await client.query(`UPDATE ${qualifiedTable} SET ${setClauses.join(", ")} WHERE ${whereClause}`, allParams);
231
+ return result.rowCount ?? 0;
232
+ }
233
+ /**
234
+ * Executes a DELETE statement for a single row with guard checks.
235
+ * Returns the number of rows deleted (0 or 1).
236
+ */
237
+ async function deleteRow(client, schema, table, pkColumns, pkValues, guardColumns, guardValues, columnTypes) {
238
+ const qualifiedTable = (0, identifiers_1.quoteQualifiedTable)(schema, table);
239
+ const pkWhere = buildPkWhereClause(pkColumns, pkValues, 1);
240
+ let allParams = [...pkWhere.params];
241
+ let whereClause = pkWhere.clause;
242
+ if (guardColumns.length > 0) {
243
+ const guardWhere = buildNullSafeWhereClause(guardColumns, guardValues, allParams.length + 1, columnTypes);
244
+ allParams = [...allParams, ...guardWhere.params];
245
+ whereClause += " AND " + guardWhere.clause;
246
+ }
247
+ const result = await client.query(`DELETE FROM ${qualifiedTable} WHERE ${whereClause}`, allParams);
248
+ return result.rowCount ?? 0;
249
+ }
250
+ //# sourceMappingURL=sql.js.map