latticesql 4.3.5 → 4.3.7

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/cli.js CHANGED
@@ -1948,6 +1948,17 @@ function rewriteParams(sql) {
1948
1948
  function rewrite(sql) {
1949
1949
  return rewriteParams(translateDialect(sql));
1950
1950
  }
1951
+ async function execQ(runner, sql, params) {
1952
+ try {
1953
+ return await runner.query(rewrite(sql), params ?? []);
1954
+ } catch (err) {
1955
+ if (err instanceof Error && !err.message.includes("[lattice-sql]")) {
1956
+ err.message += `
1957
+ [lattice-sql] failing statement: ${sql.replace(/\s+/g, " ").trim().slice(0, 300)}`;
1958
+ }
1959
+ throw err;
1960
+ }
1961
+ }
1951
1962
  var _moduleContext, SYNC_NOT_SUPPORTED_MSG, PostgresAdapter, POSTGRES_POLYFILLS;
1952
1963
  var init_postgres = __esm({
1953
1964
  "src/db/postgres.ts"() {
@@ -2026,16 +2037,16 @@ var init_postgres = __esm({
2026
2037
  // these calls await DB I/O.
2027
2038
  async runAsync(sql, params = []) {
2028
2039
  const pool = await this._readyPool();
2029
- await pool.query(rewrite(sql), params);
2040
+ await execQ(pool, sql, params);
2030
2041
  }
2031
2042
  async getAsync(sql, params = []) {
2032
2043
  const pool = await this._readyPool();
2033
- const r6 = await pool.query(rewrite(sql), params);
2044
+ const r6 = await execQ(pool, sql, params);
2034
2045
  return r6.rows[0];
2035
2046
  }
2036
2047
  async allAsync(sql, params = []) {
2037
2048
  const pool = await this._readyPool();
2038
- const r6 = await pool.query(rewrite(sql), params);
2049
+ const r6 = await execQ(pool, sql, params);
2039
2050
  return r6.rows;
2040
2051
  }
2041
2052
  /**
@@ -2053,21 +2064,20 @@ var init_postgres = __esm({
2053
2064
  * client for the transaction lifetime and avoid the per-call setup.
2054
2065
  */
2055
2066
  prepareAsync(sql) {
2056
- const rewritten = rewrite(sql);
2057
2067
  return {
2058
2068
  run: async (...params) => {
2059
2069
  const pool = await this._readyPool();
2060
- const r6 = await pool.query(rewritten, params);
2070
+ const r6 = await execQ(pool, sql, params);
2061
2071
  return { changes: r6.rowCount ?? 0, lastInsertRowid: 0 };
2062
2072
  },
2063
2073
  get: async (...params) => {
2064
2074
  const pool = await this._readyPool();
2065
- const r6 = await pool.query(rewritten, params);
2075
+ const r6 = await execQ(pool, sql, params);
2066
2076
  return r6.rows[0];
2067
2077
  },
2068
2078
  all: async (...params) => {
2069
2079
  const pool = await this._readyPool();
2070
- const r6 = await pool.query(rewritten, params);
2080
+ const r6 = await execQ(pool, sql, params);
2071
2081
  return r6.rows;
2072
2082
  }
2073
2083
  };
@@ -2117,7 +2127,7 @@ var init_postgres = __esm({
2117
2127
  if (upper.includes("PRIMARY KEY")) return;
2118
2128
  const translated = translateTypeSpec(typeSpec);
2119
2129
  const pool = await this._readyPool();
2120
- await pool.query(`ALTER TABLE "${table}" ADD COLUMN IF NOT EXISTS "${column}" ${translated}`);
2130
+ await execQ(pool, `ALTER TABLE "${table}" ADD COLUMN IF NOT EXISTS "${column}" ${translated}`);
2121
2131
  }
2122
2132
  /**
2123
2133
  * Run `fn` against a single checked-out pool client wrapped in BEGIN/COMMIT.
@@ -2135,15 +2145,15 @@ var init_postgres = __esm({
2135
2145
  const client = await pool.connect();
2136
2146
  const tx = {
2137
2147
  run: async (sql, params) => {
2138
- const r6 = await client.query(rewrite(sql), params ?? []);
2148
+ const r6 = await execQ(client, sql, params);
2139
2149
  return { changes: r6.rowCount ?? 0 };
2140
2150
  },
2141
2151
  get: async (sql, params) => {
2142
- const r6 = await client.query(rewrite(sql), params ?? []);
2152
+ const r6 = await execQ(client, sql, params);
2143
2153
  return r6.rows[0];
2144
2154
  },
2145
2155
  all: async (sql, params) => {
2146
- const r6 = await client.query(rewrite(sql), params ?? []);
2156
+ const r6 = await execQ(client, sql, params);
2147
2157
  return r6.rows;
2148
2158
  }
2149
2159
  };
@@ -58434,14 +58444,40 @@ async function normalizeEmptyDeletedAt(db) {
58434
58444
  await db.migrate([
58435
58445
  {
58436
58446
  version: `${DELETED_AT_SENTINEL}:all`,
58447
+ // TYPE-AWARE: only a text-like deleted_at column can hold the legacy ''
58448
+ // sentinel. A column that is a real `timestamptz` (some clouds' deleted_at
58449
+ // is) MUST be skipped: the predicate `deleted_at = ''` forces Postgres to
58450
+ // parse `''::timestamptz` at PLAN time — invalid input that throws regardless
58451
+ // of data (a timestamptz column can't even hold ''), which aborted the entire
58452
+ // workspace open. We BLACKLIST the non-text types (timestamp/date/time,
58453
+ // numeric, boolean, json, uuid, bytea, xml) rather than allow-list text — so
58454
+ // text-like columns we don't enumerate (citext, a DOMAIN over text, a custom
58455
+ // text type) are still normalized instead of silently leaving '' rows that
58456
+ // 4.x's `deleted_at IS NULL` predicate would read as DELETED. PER-TABLE FAULT
58457
+ // ISOLATION is the backstop: each table's UPDATE runs in its own
58458
+ // subtransaction, so an exotic type that still errors on `= ''` (enum, inet,
58459
+ // interval, array) — or a blocking trigger/lock — is warned + skipped, never
58460
+ // fatal to the open.
58437
58461
  sql: `DO $LATTICE_DAU$
58438
58462
  DECLARE r record;
58439
58463
  BEGIN
58440
58464
  FOR r IN
58441
58465
  SELECT table_name FROM information_schema.columns
58442
- WHERE table_schema = current_schema() AND column_name = 'deleted_at'
58466
+ WHERE table_schema = current_schema()
58467
+ AND column_name = 'deleted_at'
58468
+ AND data_type NOT IN (
58469
+ 'timestamp with time zone', 'timestamp without time zone',
58470
+ 'date', 'time with time zone', 'time without time zone',
58471
+ 'integer', 'bigint', 'smallint', 'numeric', 'decimal',
58472
+ 'real', 'double precision', 'boolean', 'json', 'jsonb',
58473
+ 'bytea', 'uuid', 'xml'
58474
+ )
58443
58475
  LOOP
58444
- EXECUTE format('UPDATE %I SET deleted_at = NULL WHERE deleted_at = ''''', r.table_name);
58476
+ BEGIN
58477
+ EXECUTE format('UPDATE %I SET deleted_at = NULL WHERE deleted_at = ''''', r.table_name);
58478
+ EXCEPTION WHEN others THEN
58479
+ RAISE WARNING 'lattice: skipped deleted_at normalization for %: %', r.table_name, SQLERRM;
58480
+ END;
58445
58481
  END LOOP;
58446
58482
  END $LATTICE_DAU$;`
58447
58483
  }
@@ -82096,7 +82132,7 @@ function printHelp() {
82096
82132
  );
82097
82133
  }
82098
82134
  function getVersion() {
82099
- if (true) return "4.3.5";
82135
+ if (true) return "4.3.7";
82100
82136
  try {
82101
82137
  const pkgPath = new URL("../package.json", import.meta.url).pathname;
82102
82138
  const pkg = JSON.parse(readFileSync26(pkgPath, "utf-8"));
@@ -2030,6 +2030,17 @@ function rewriteParams(sql) {
2030
2030
  function rewrite(sql) {
2031
2031
  return rewriteParams(translateDialect(sql));
2032
2032
  }
2033
+ async function execQ(runner, sql, params) {
2034
+ try {
2035
+ return await runner.query(rewrite(sql), params ?? []);
2036
+ } catch (err) {
2037
+ if (err instanceof Error && !err.message.includes("[lattice-sql]")) {
2038
+ err.message += `
2039
+ [lattice-sql] failing statement: ${sql.replace(/\s+/g, " ").trim().slice(0, 300)}`;
2040
+ }
2041
+ throw err;
2042
+ }
2043
+ }
2033
2044
  var _moduleContext, SYNC_NOT_SUPPORTED_MSG, PostgresAdapter, POSTGRES_POLYFILLS;
2034
2045
  var init_postgres = __esm({
2035
2046
  "src/db/postgres.ts"() {
@@ -2108,16 +2119,16 @@ var init_postgres = __esm({
2108
2119
  // these calls await DB I/O.
2109
2120
  async runAsync(sql, params = []) {
2110
2121
  const pool = await this._readyPool();
2111
- await pool.query(rewrite(sql), params);
2122
+ await execQ(pool, sql, params);
2112
2123
  }
2113
2124
  async getAsync(sql, params = []) {
2114
2125
  const pool = await this._readyPool();
2115
- const r6 = await pool.query(rewrite(sql), params);
2126
+ const r6 = await execQ(pool, sql, params);
2116
2127
  return r6.rows[0];
2117
2128
  }
2118
2129
  async allAsync(sql, params = []) {
2119
2130
  const pool = await this._readyPool();
2120
- const r6 = await pool.query(rewrite(sql), params);
2131
+ const r6 = await execQ(pool, sql, params);
2121
2132
  return r6.rows;
2122
2133
  }
2123
2134
  /**
@@ -2135,21 +2146,20 @@ var init_postgres = __esm({
2135
2146
  * client for the transaction lifetime and avoid the per-call setup.
2136
2147
  */
2137
2148
  prepareAsync(sql) {
2138
- const rewritten = rewrite(sql);
2139
2149
  return {
2140
2150
  run: async (...params) => {
2141
2151
  const pool = await this._readyPool();
2142
- const r6 = await pool.query(rewritten, params);
2152
+ const r6 = await execQ(pool, sql, params);
2143
2153
  return { changes: r6.rowCount ?? 0, lastInsertRowid: 0 };
2144
2154
  },
2145
2155
  get: async (...params) => {
2146
2156
  const pool = await this._readyPool();
2147
- const r6 = await pool.query(rewritten, params);
2157
+ const r6 = await execQ(pool, sql, params);
2148
2158
  return r6.rows[0];
2149
2159
  },
2150
2160
  all: async (...params) => {
2151
2161
  const pool = await this._readyPool();
2152
- const r6 = await pool.query(rewritten, params);
2162
+ const r6 = await execQ(pool, sql, params);
2153
2163
  return r6.rows;
2154
2164
  }
2155
2165
  };
@@ -2199,7 +2209,7 @@ var init_postgres = __esm({
2199
2209
  if (upper.includes("PRIMARY KEY")) return;
2200
2210
  const translated = translateTypeSpec(typeSpec);
2201
2211
  const pool = await this._readyPool();
2202
- await pool.query(`ALTER TABLE "${table}" ADD COLUMN IF NOT EXISTS "${column}" ${translated}`);
2212
+ await execQ(pool, `ALTER TABLE "${table}" ADD COLUMN IF NOT EXISTS "${column}" ${translated}`);
2203
2213
  }
2204
2214
  /**
2205
2215
  * Run `fn` against a single checked-out pool client wrapped in BEGIN/COMMIT.
@@ -2217,15 +2227,15 @@ var init_postgres = __esm({
2217
2227
  const client = await pool.connect();
2218
2228
  const tx = {
2219
2229
  run: async (sql, params) => {
2220
- const r6 = await client.query(rewrite(sql), params ?? []);
2230
+ const r6 = await execQ(client, sql, params);
2221
2231
  return { changes: r6.rowCount ?? 0 };
2222
2232
  },
2223
2233
  get: async (sql, params) => {
2224
- const r6 = await client.query(rewrite(sql), params ?? []);
2234
+ const r6 = await execQ(client, sql, params);
2225
2235
  return r6.rows[0];
2226
2236
  },
2227
2237
  all: async (sql, params) => {
2228
- const r6 = await client.query(rewrite(sql), params ?? []);
2238
+ const r6 = await execQ(client, sql, params);
2229
2239
  return r6.rows;
2230
2240
  }
2231
2241
  };
@@ -58128,14 +58138,40 @@ async function normalizeEmptyDeletedAt(db) {
58128
58138
  await db.migrate([
58129
58139
  {
58130
58140
  version: `${DELETED_AT_SENTINEL}:all`,
58141
+ // TYPE-AWARE: only a text-like deleted_at column can hold the legacy ''
58142
+ // sentinel. A column that is a real `timestamptz` (some clouds' deleted_at
58143
+ // is) MUST be skipped: the predicate `deleted_at = ''` forces Postgres to
58144
+ // parse `''::timestamptz` at PLAN time — invalid input that throws regardless
58145
+ // of data (a timestamptz column can't even hold ''), which aborted the entire
58146
+ // workspace open. We BLACKLIST the non-text types (timestamp/date/time,
58147
+ // numeric, boolean, json, uuid, bytea, xml) rather than allow-list text — so
58148
+ // text-like columns we don't enumerate (citext, a DOMAIN over text, a custom
58149
+ // text type) are still normalized instead of silently leaving '' rows that
58150
+ // 4.x's `deleted_at IS NULL` predicate would read as DELETED. PER-TABLE FAULT
58151
+ // ISOLATION is the backstop: each table's UPDATE runs in its own
58152
+ // subtransaction, so an exotic type that still errors on `= ''` (enum, inet,
58153
+ // interval, array) — or a blocking trigger/lock — is warned + skipped, never
58154
+ // fatal to the open.
58131
58155
  sql: `DO $LATTICE_DAU$
58132
58156
  DECLARE r record;
58133
58157
  BEGIN
58134
58158
  FOR r IN
58135
58159
  SELECT table_name FROM information_schema.columns
58136
- WHERE table_schema = current_schema() AND column_name = 'deleted_at'
58160
+ WHERE table_schema = current_schema()
58161
+ AND column_name = 'deleted_at'
58162
+ AND data_type NOT IN (
58163
+ 'timestamp with time zone', 'timestamp without time zone',
58164
+ 'date', 'time with time zone', 'time without time zone',
58165
+ 'integer', 'bigint', 'smallint', 'numeric', 'decimal',
58166
+ 'real', 'double precision', 'boolean', 'json', 'jsonb',
58167
+ 'bytea', 'uuid', 'xml'
58168
+ )
58137
58169
  LOOP
58138
- EXECUTE format('UPDATE %I SET deleted_at = NULL WHERE deleted_at = ''''', r.table_name);
58170
+ BEGIN
58171
+ EXECUTE format('UPDATE %I SET deleted_at = NULL WHERE deleted_at = ''''', r.table_name);
58172
+ EXCEPTION WHEN others THEN
58173
+ RAISE WARNING 'lattice: skipped deleted_at normalization for %: %', r.table_name, SQLERRM;
58174
+ END;
58139
58175
  END LOOP;
58140
58176
  END $LATTICE_DAU$;`
58141
58177
  }
@@ -81674,7 +81710,7 @@ ${e6.stack ?? ""}`
81674
81710
  }
81675
81711
 
81676
81712
  // src/desktop-entry.ts
81677
- var VERSION2 = true ? "4.3.5" : "unknown";
81713
+ var VERSION2 = true ? "4.3.7" : "unknown";
81678
81714
  export {
81679
81715
  VERSION2 as VERSION,
81680
81716
  ensureRootForGui,
package/dist/index.cjs CHANGED
@@ -1161,6 +1161,17 @@ function rewriteParams(sql) {
1161
1161
  function rewrite(sql) {
1162
1162
  return rewriteParams(translateDialect(sql));
1163
1163
  }
1164
+ async function execQ(runner, sql, params) {
1165
+ try {
1166
+ return await runner.query(rewrite(sql), params ?? []);
1167
+ } catch (err) {
1168
+ if (err instanceof Error && !err.message.includes("[lattice-sql]")) {
1169
+ err.message += `
1170
+ [lattice-sql] failing statement: ${sql.replace(/\s+/g, " ").trim().slice(0, 300)}`;
1171
+ }
1172
+ throw err;
1173
+ }
1174
+ }
1164
1175
  var import_node_path4, import_node_url, import_node_module3, import_meta3, _moduleContext, SYNC_NOT_SUPPORTED_MSG, PostgresAdapter, POSTGRES_POLYFILLS;
1165
1176
  var init_postgres = __esm({
1166
1177
  "src/db/postgres.ts"() {
@@ -1243,16 +1254,16 @@ var init_postgres = __esm({
1243
1254
  // these calls await DB I/O.
1244
1255
  async runAsync(sql, params = []) {
1245
1256
  const pool = await this._readyPool();
1246
- await pool.query(rewrite(sql), params);
1257
+ await execQ(pool, sql, params);
1247
1258
  }
1248
1259
  async getAsync(sql, params = []) {
1249
1260
  const pool = await this._readyPool();
1250
- const r6 = await pool.query(rewrite(sql), params);
1261
+ const r6 = await execQ(pool, sql, params);
1251
1262
  return r6.rows[0];
1252
1263
  }
1253
1264
  async allAsync(sql, params = []) {
1254
1265
  const pool = await this._readyPool();
1255
- const r6 = await pool.query(rewrite(sql), params);
1266
+ const r6 = await execQ(pool, sql, params);
1256
1267
  return r6.rows;
1257
1268
  }
1258
1269
  /**
@@ -1270,21 +1281,20 @@ var init_postgres = __esm({
1270
1281
  * client for the transaction lifetime and avoid the per-call setup.
1271
1282
  */
1272
1283
  prepareAsync(sql) {
1273
- const rewritten = rewrite(sql);
1274
1284
  return {
1275
1285
  run: async (...params) => {
1276
1286
  const pool = await this._readyPool();
1277
- const r6 = await pool.query(rewritten, params);
1287
+ const r6 = await execQ(pool, sql, params);
1278
1288
  return { changes: r6.rowCount ?? 0, lastInsertRowid: 0 };
1279
1289
  },
1280
1290
  get: async (...params) => {
1281
1291
  const pool = await this._readyPool();
1282
- const r6 = await pool.query(rewritten, params);
1292
+ const r6 = await execQ(pool, sql, params);
1283
1293
  return r6.rows[0];
1284
1294
  },
1285
1295
  all: async (...params) => {
1286
1296
  const pool = await this._readyPool();
1287
- const r6 = await pool.query(rewritten, params);
1297
+ const r6 = await execQ(pool, sql, params);
1288
1298
  return r6.rows;
1289
1299
  }
1290
1300
  };
@@ -1334,7 +1344,7 @@ var init_postgres = __esm({
1334
1344
  if (upper.includes("PRIMARY KEY")) return;
1335
1345
  const translated = translateTypeSpec(typeSpec);
1336
1346
  const pool = await this._readyPool();
1337
- await pool.query(`ALTER TABLE "${table}" ADD COLUMN IF NOT EXISTS "${column}" ${translated}`);
1347
+ await execQ(pool, `ALTER TABLE "${table}" ADD COLUMN IF NOT EXISTS "${column}" ${translated}`);
1338
1348
  }
1339
1349
  /**
1340
1350
  * Run `fn` against a single checked-out pool client wrapped in BEGIN/COMMIT.
@@ -1352,15 +1362,15 @@ var init_postgres = __esm({
1352
1362
  const client = await pool.connect();
1353
1363
  const tx = {
1354
1364
  run: async (sql, params) => {
1355
- const r6 = await client.query(rewrite(sql), params ?? []);
1365
+ const r6 = await execQ(client, sql, params);
1356
1366
  return { changes: r6.rowCount ?? 0 };
1357
1367
  },
1358
1368
  get: async (sql, params) => {
1359
- const r6 = await client.query(rewrite(sql), params ?? []);
1369
+ const r6 = await execQ(client, sql, params);
1360
1370
  return r6.rows[0];
1361
1371
  },
1362
1372
  all: async (sql, params) => {
1363
- const r6 = await client.query(rewrite(sql), params ?? []);
1373
+ const r6 = await execQ(client, sql, params);
1364
1374
  return r6.rows;
1365
1375
  }
1366
1376
  };
@@ -62665,14 +62675,40 @@ async function normalizeEmptyDeletedAt(db) {
62665
62675
  await db.migrate([
62666
62676
  {
62667
62677
  version: `${DELETED_AT_SENTINEL}:all`,
62678
+ // TYPE-AWARE: only a text-like deleted_at column can hold the legacy ''
62679
+ // sentinel. A column that is a real `timestamptz` (some clouds' deleted_at
62680
+ // is) MUST be skipped: the predicate `deleted_at = ''` forces Postgres to
62681
+ // parse `''::timestamptz` at PLAN time — invalid input that throws regardless
62682
+ // of data (a timestamptz column can't even hold ''), which aborted the entire
62683
+ // workspace open. We BLACKLIST the non-text types (timestamp/date/time,
62684
+ // numeric, boolean, json, uuid, bytea, xml) rather than allow-list text — so
62685
+ // text-like columns we don't enumerate (citext, a DOMAIN over text, a custom
62686
+ // text type) are still normalized instead of silently leaving '' rows that
62687
+ // 4.x's `deleted_at IS NULL` predicate would read as DELETED. PER-TABLE FAULT
62688
+ // ISOLATION is the backstop: each table's UPDATE runs in its own
62689
+ // subtransaction, so an exotic type that still errors on `= ''` (enum, inet,
62690
+ // interval, array) — or a blocking trigger/lock — is warned + skipped, never
62691
+ // fatal to the open.
62668
62692
  sql: `DO $LATTICE_DAU$
62669
62693
  DECLARE r record;
62670
62694
  BEGIN
62671
62695
  FOR r IN
62672
62696
  SELECT table_name FROM information_schema.columns
62673
- WHERE table_schema = current_schema() AND column_name = 'deleted_at'
62697
+ WHERE table_schema = current_schema()
62698
+ AND column_name = 'deleted_at'
62699
+ AND data_type NOT IN (
62700
+ 'timestamp with time zone', 'timestamp without time zone',
62701
+ 'date', 'time with time zone', 'time without time zone',
62702
+ 'integer', 'bigint', 'smallint', 'numeric', 'decimal',
62703
+ 'real', 'double precision', 'boolean', 'json', 'jsonb',
62704
+ 'bytea', 'uuid', 'xml'
62705
+ )
62674
62706
  LOOP
62675
- EXECUTE format('UPDATE %I SET deleted_at = NULL WHERE deleted_at = ''''', r.table_name);
62707
+ BEGIN
62708
+ EXECUTE format('UPDATE %I SET deleted_at = NULL WHERE deleted_at = ''''', r.table_name);
62709
+ EXCEPTION WHEN others THEN
62710
+ RAISE WARNING 'lattice: skipped deleted_at normalization for %: %', r.table_name, SQLERRM;
62711
+ END;
62676
62712
  END LOOP;
62677
62713
  END $LATTICE_DAU$;`
62678
62714
  }
package/dist/index.js CHANGED
@@ -1153,6 +1153,17 @@ function rewriteParams(sql) {
1153
1153
  function rewrite(sql) {
1154
1154
  return rewriteParams(translateDialect(sql));
1155
1155
  }
1156
+ async function execQ(runner, sql, params) {
1157
+ try {
1158
+ return await runner.query(rewrite(sql), params ?? []);
1159
+ } catch (err) {
1160
+ if (err instanceof Error && !err.message.includes("[lattice-sql]")) {
1161
+ err.message += `
1162
+ [lattice-sql] failing statement: ${sql.replace(/\s+/g, " ").trim().slice(0, 300)}`;
1163
+ }
1164
+ throw err;
1165
+ }
1166
+ }
1156
1167
  var _moduleContext, SYNC_NOT_SUPPORTED_MSG, PostgresAdapter, POSTGRES_POLYFILLS;
1157
1168
  var init_postgres = __esm({
1158
1169
  "src/db/postgres.ts"() {
@@ -1231,16 +1242,16 @@ var init_postgres = __esm({
1231
1242
  // these calls await DB I/O.
1232
1243
  async runAsync(sql, params = []) {
1233
1244
  const pool = await this._readyPool();
1234
- await pool.query(rewrite(sql), params);
1245
+ await execQ(pool, sql, params);
1235
1246
  }
1236
1247
  async getAsync(sql, params = []) {
1237
1248
  const pool = await this._readyPool();
1238
- const r6 = await pool.query(rewrite(sql), params);
1249
+ const r6 = await execQ(pool, sql, params);
1239
1250
  return r6.rows[0];
1240
1251
  }
1241
1252
  async allAsync(sql, params = []) {
1242
1253
  const pool = await this._readyPool();
1243
- const r6 = await pool.query(rewrite(sql), params);
1254
+ const r6 = await execQ(pool, sql, params);
1244
1255
  return r6.rows;
1245
1256
  }
1246
1257
  /**
@@ -1258,21 +1269,20 @@ var init_postgres = __esm({
1258
1269
  * client for the transaction lifetime and avoid the per-call setup.
1259
1270
  */
1260
1271
  prepareAsync(sql) {
1261
- const rewritten = rewrite(sql);
1262
1272
  return {
1263
1273
  run: async (...params) => {
1264
1274
  const pool = await this._readyPool();
1265
- const r6 = await pool.query(rewritten, params);
1275
+ const r6 = await execQ(pool, sql, params);
1266
1276
  return { changes: r6.rowCount ?? 0, lastInsertRowid: 0 };
1267
1277
  },
1268
1278
  get: async (...params) => {
1269
1279
  const pool = await this._readyPool();
1270
- const r6 = await pool.query(rewritten, params);
1280
+ const r6 = await execQ(pool, sql, params);
1271
1281
  return r6.rows[0];
1272
1282
  },
1273
1283
  all: async (...params) => {
1274
1284
  const pool = await this._readyPool();
1275
- const r6 = await pool.query(rewritten, params);
1285
+ const r6 = await execQ(pool, sql, params);
1276
1286
  return r6.rows;
1277
1287
  }
1278
1288
  };
@@ -1322,7 +1332,7 @@ var init_postgres = __esm({
1322
1332
  if (upper.includes("PRIMARY KEY")) return;
1323
1333
  const translated = translateTypeSpec(typeSpec);
1324
1334
  const pool = await this._readyPool();
1325
- await pool.query(`ALTER TABLE "${table}" ADD COLUMN IF NOT EXISTS "${column}" ${translated}`);
1335
+ await execQ(pool, `ALTER TABLE "${table}" ADD COLUMN IF NOT EXISTS "${column}" ${translated}`);
1326
1336
  }
1327
1337
  /**
1328
1338
  * Run `fn` against a single checked-out pool client wrapped in BEGIN/COMMIT.
@@ -1340,15 +1350,15 @@ var init_postgres = __esm({
1340
1350
  const client = await pool.connect();
1341
1351
  const tx = {
1342
1352
  run: async (sql, params) => {
1343
- const r6 = await client.query(rewrite(sql), params ?? []);
1353
+ const r6 = await execQ(client, sql, params);
1344
1354
  return { changes: r6.rowCount ?? 0 };
1345
1355
  },
1346
1356
  get: async (sql, params) => {
1347
- const r6 = await client.query(rewrite(sql), params ?? []);
1357
+ const r6 = await execQ(client, sql, params);
1348
1358
  return r6.rows[0];
1349
1359
  },
1350
1360
  all: async (sql, params) => {
1351
- const r6 = await client.query(rewrite(sql), params ?? []);
1361
+ const r6 = await execQ(client, sql, params);
1352
1362
  return r6.rows;
1353
1363
  }
1354
1364
  };
@@ -62365,14 +62375,40 @@ async function normalizeEmptyDeletedAt(db) {
62365
62375
  await db.migrate([
62366
62376
  {
62367
62377
  version: `${DELETED_AT_SENTINEL}:all`,
62378
+ // TYPE-AWARE: only a text-like deleted_at column can hold the legacy ''
62379
+ // sentinel. A column that is a real `timestamptz` (some clouds' deleted_at
62380
+ // is) MUST be skipped: the predicate `deleted_at = ''` forces Postgres to
62381
+ // parse `''::timestamptz` at PLAN time — invalid input that throws regardless
62382
+ // of data (a timestamptz column can't even hold ''), which aborted the entire
62383
+ // workspace open. We BLACKLIST the non-text types (timestamp/date/time,
62384
+ // numeric, boolean, json, uuid, bytea, xml) rather than allow-list text — so
62385
+ // text-like columns we don't enumerate (citext, a DOMAIN over text, a custom
62386
+ // text type) are still normalized instead of silently leaving '' rows that
62387
+ // 4.x's `deleted_at IS NULL` predicate would read as DELETED. PER-TABLE FAULT
62388
+ // ISOLATION is the backstop: each table's UPDATE runs in its own
62389
+ // subtransaction, so an exotic type that still errors on `= ''` (enum, inet,
62390
+ // interval, array) — or a blocking trigger/lock — is warned + skipped, never
62391
+ // fatal to the open.
62368
62392
  sql: `DO $LATTICE_DAU$
62369
62393
  DECLARE r record;
62370
62394
  BEGIN
62371
62395
  FOR r IN
62372
62396
  SELECT table_name FROM information_schema.columns
62373
- WHERE table_schema = current_schema() AND column_name = 'deleted_at'
62397
+ WHERE table_schema = current_schema()
62398
+ AND column_name = 'deleted_at'
62399
+ AND data_type NOT IN (
62400
+ 'timestamp with time zone', 'timestamp without time zone',
62401
+ 'date', 'time with time zone', 'time without time zone',
62402
+ 'integer', 'bigint', 'smallint', 'numeric', 'decimal',
62403
+ 'real', 'double precision', 'boolean', 'json', 'jsonb',
62404
+ 'bytea', 'uuid', 'xml'
62405
+ )
62374
62406
  LOOP
62375
- EXECUTE format('UPDATE %I SET deleted_at = NULL WHERE deleted_at = ''''', r.table_name);
62407
+ BEGIN
62408
+ EXECUTE format('UPDATE %I SET deleted_at = NULL WHERE deleted_at = ''''', r.table_name);
62409
+ EXCEPTION WHEN others THEN
62410
+ RAISE WARNING 'lattice: skipped deleted_at normalization for %: %', r.table_name, SQLERRM;
62411
+ END;
62376
62412
  END LOOP;
62377
62413
  END $LATTICE_DAU$;`
62378
62414
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "latticesql",
3
- "version": "4.3.5",
3
+ "version": "4.3.7",
4
4
  "description": "Persistent structured memory for AI agent systems — pluggable SQLite or Postgres backend, LLM context bridge",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",