@crvouga/sqlite-mem 1.5.0 → 1.6.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.
@@ -818,6 +818,11 @@ const TAIL: CatalogSection[] = [
818
818
  ["win-01", "window frame fuzz", F],
819
819
  ["up-01", "UPSERT fuzz", F],
820
820
  ["date-01", "date modifier fuzz", F],
821
+ ["trig-01", "BEFORE/AFTER trigger + RAISE fuzz", F],
822
+ ["genc-01", "STORED/VIRTUAL generated column fuzz", F],
823
+ ["notin-01", "NOT IN / IN NULL + scalar cardinality fuzz", F],
824
+ ["cnt-01", "changes/total_changes/last_insert_rowid fuzz", F],
825
+ ["fkedge-01", "FK SET NULL/DEFAULT/MATCH edge fuzz", F],
821
826
  ["tlp-01", "ternary logic partitioning metamorphic", F],
822
827
  ["norec-01", "NoREC metamorphic rewrite", F],
823
828
  ["robust-01", "SqliteError-only / timeout / SQLM bit-flip", F],
package/dist/index.js CHANGED
@@ -1677,10 +1677,19 @@ var Parser = class {
1677
1677
  parseFkActions() {
1678
1678
  let onDelete = null;
1679
1679
  let onUpdate = null;
1680
+ while (this.match("MATCH")) {
1681
+ this.parseIdent();
1682
+ }
1680
1683
  while (this.at("ON")) {
1681
1684
  if (this.peek().kind === "DELETE") onDelete = this.parseFkAction("DELETE");
1682
1685
  else if (this.peek().kind === "UPDATE") onUpdate = this.parseFkAction("UPDATE");
1683
1686
  else this.syntaxError("expected ON DELETE or ON UPDATE");
1687
+ while (this.match("MATCH")) {
1688
+ this.parseIdent();
1689
+ }
1690
+ }
1691
+ while (this.match("MATCH")) {
1692
+ this.parseIdent();
1684
1693
  }
1685
1694
  return { onDelete, onUpdate };
1686
1695
  }
@@ -9899,11 +9908,13 @@ function pragmaTableInfo(args, env, xinfo) {
9899
9908
  const tableName = requireNameArg(args, "table_info");
9900
9909
  const table = env.state.tables.get(tableName.toLowerCase());
9901
9910
  if (!table) return { columns: xinfoColumns(xinfo), rows: [] };
9902
- const rows = table.columns.map((column, cid) => {
9911
+ const visible = xinfo ? table.columns : table.columns.filter((column) => !column.generated);
9912
+ const rows = visible.map((column, cid) => {
9903
9913
  const pkIndex = table.columns.filter((c) => c.primaryKey).findIndex((c) => c.name === column.name);
9904
9914
  const pk = column.primaryKey ? pkIndex >= 0 ? pkIndex + 1 : 1 : 0;
9905
9915
  const dflt = column.defaultExpr ? defaultLiteral(column.defaultExpr) : null;
9906
- const base = [cid, column.name, column.typeName ?? "", column.notNull ? 1 : 0, dflt, pk];
9916
+ const notNull = column.notNull || table.withoutRowid && column.primaryKey ? 1 : 0;
9917
+ const base = [cid, column.name, column.typeName ?? "", notNull, dflt, pk];
9907
9918
  if (xinfo) {
9908
9919
  let hidden = 0;
9909
9920
  if (column.generated && !column.generated.stored) hidden = 2;
@@ -11362,10 +11373,20 @@ function scanFrom(item, env, parent) {
11362
11373
  if (item.joinType === "RIGHT" || item.joinType === "FULL") {
11363
11374
  right.forEach((rhs, rightIndex) => {
11364
11375
  if (matchedRight.has(rightIndex)) return;
11365
- const rightCells = using ? rhs.cells.map(
11366
- (cell) => using.some((name) => name.toLowerCase() === cell.name.toLowerCase()) ? { ...cell, hiddenByUsing: true } : cell
11367
- ) : rhs.cells;
11368
- result.push({ cells: [...nullLeft, ...rightCells] });
11376
+ if (using) {
11377
+ const leftCells = nullLeft.map((cell) => {
11378
+ const shared = using.some((name) => name.toLowerCase() === cell.name.toLowerCase());
11379
+ if (!shared) return cell;
11380
+ const fromRight = rhs.cells.find((c) => c.name.toLowerCase() === cell.name.toLowerCase());
11381
+ return { ...cell, value: fromRight?.value ?? null, hiddenByUsing: false };
11382
+ });
11383
+ const rightOnly = rhs.cells.filter(
11384
+ (cell) => !using.some((name) => name.toLowerCase() === cell.name.toLowerCase())
11385
+ );
11386
+ result.push({ cells: [...leftCells, ...rightOnly] });
11387
+ } else {
11388
+ result.push({ cells: [...nullLeft, ...rhs.cells] });
11389
+ }
11369
11390
  });
11370
11391
  }
11371
11392
  return result;
@@ -13266,17 +13287,28 @@ function checkForeignKeys(table, row, env) {
13266
13287
  function fkIsDeferred(constraint, env) {
13267
13288
  return env.transactions.inTransaction && constraint.initiallyDeferred;
13268
13289
  }
13269
- function assertForeignKeySatisfied(row, constraint, env) {
13290
+ function assertForeignKeySatisfied(row, constraint, env, excludeParent = null) {
13270
13291
  const values = constraint.columns.map((name) => row.values.get(normalizeColumnName(name)) ?? null);
13292
+ assertForeignKeyValues(values, constraint, env, excludeParent);
13293
+ }
13294
+ function assertForeignKeySatisfiedWithUpdates(row, constraint, updates, env, excludeParent) {
13295
+ const values = constraint.columns.map((name) => {
13296
+ const key = normalizeColumnName(name);
13297
+ return updates.has(key) ? updates.get(key) ?? null : row.values.get(key) ?? null;
13298
+ });
13299
+ assertForeignKeyValues(values, constraint, env, excludeParent);
13300
+ }
13301
+ function assertForeignKeyValues(values, constraint, env, excludeParent) {
13271
13302
  if (values.some((value) => value === null)) return;
13272
13303
  const parent = env.state.getTable(constraint.refTable);
13273
13304
  const parentColumns = constraint.refColumns ?? parent.columns.filter((column) => column.primaryKey).map((column) => column.name);
13274
- if (![...parent.scan()].some(
13275
- (candidate) => values.every((value, index) => {
13305
+ if (![...parent.scan()].some((candidate) => {
13306
+ if (excludeParent && candidate.rowid === excludeParent.rowid) return false;
13307
+ return values.every((value, index) => {
13276
13308
  const parentColumn = parentColumns[index];
13277
13309
  return parentColumn !== void 0 && compareSql(value, candidate.values.get(normalizeColumnName(parentColumn)) ?? null) === 0;
13278
- })
13279
- )) {
13310
+ });
13311
+ })) {
13280
13312
  throw new SqliteError("FOREIGN KEY constraint failed", "constraint_foreign", "SQLITE_CONSTRAINT_FOREIGNKEY");
13281
13313
  }
13282
13314
  }
@@ -13317,7 +13349,9 @@ function applyReferentialDelete(parent, row, env) {
13317
13349
  );
13318
13350
  changes += 1 + updated.cascaded;
13319
13351
  } else if (constraint.onDelete === "SET DEFAULT") {
13320
- const updated = updateOne(child, candidate, defaultUpdates(child, constraint.columns, env), env);
13352
+ const updates = defaultUpdates(child, constraint.columns, env);
13353
+ assertForeignKeySatisfiedWithUpdates(candidate, constraint, updates, env, row);
13354
+ const updated = updateOne(child, candidate, updates, env);
13321
13355
  changes += 1 + updated.cascaded;
13322
13356
  } else if (constraint.onDelete === "RESTRICT" || !fkIsDeferred(constraint, env)) {
13323
13357
  throw new SqliteError("FOREIGN KEY constraint failed", "constraint_foreign", "SQLITE_CONSTRAINT_FOREIGNKEY");