@quereus/quereus 0.13.1 → 0.15.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 +18 -11
- package/dist/src/core/database-assertions.d.ts +54 -0
- package/dist/src/core/database-assertions.d.ts.map +1 -0
- package/dist/src/core/database-assertions.js +237 -0
- package/dist/src/core/database-assertions.js.map +1 -0
- package/dist/src/core/database-events.d.ts +219 -0
- package/dist/src/core/database-events.d.ts.map +1 -0
- package/dist/src/core/database-events.js +368 -0
- package/dist/src/core/database-events.js.map +1 -0
- package/dist/src/core/database-transaction.d.ts +126 -0
- package/dist/src/core/database-transaction.d.ts.map +1 -0
- package/dist/src/core/database-transaction.js +345 -0
- package/dist/src/core/database-transaction.js.map +1 -0
- package/dist/src/core/database.d.ts +100 -34
- package/dist/src/core/database.d.ts.map +1 -1
- package/dist/src/core/database.js +435 -475
- package/dist/src/core/database.js.map +1 -1
- package/dist/src/core/statement.d.ts +12 -2
- package/dist/src/core/statement.d.ts.map +1 -1
- package/dist/src/core/statement.js +94 -42
- package/dist/src/core/statement.js.map +1 -1
- package/dist/src/core/utils.d.ts +4 -0
- package/dist/src/core/utils.d.ts.map +1 -0
- package/dist/src/core/utils.js +9 -0
- package/dist/src/core/utils.js.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/parser/parser.d.ts.map +1 -1
- package/dist/src/parser/parser.js +17 -0
- package/dist/src/parser/parser.js.map +1 -1
- package/dist/src/runtime/emit/create-assertion.d.ts.map +1 -1
- package/dist/src/runtime/emit/create-assertion.js +2 -0
- package/dist/src/runtime/emit/create-assertion.js.map +1 -1
- package/dist/src/runtime/emit/create-index.d.ts.map +1 -1
- package/dist/src/runtime/emit/create-index.js +2 -0
- package/dist/src/runtime/emit/create-index.js.map +1 -1
- package/dist/src/runtime/emit/create-table.d.ts.map +1 -1
- package/dist/src/runtime/emit/create-table.js +2 -0
- package/dist/src/runtime/emit/create-table.js.map +1 -1
- package/dist/src/runtime/emit/create-view.d.ts.map +1 -1
- package/dist/src/runtime/emit/create-view.js +2 -0
- package/dist/src/runtime/emit/create-view.js.map +1 -1
- package/dist/src/runtime/emit/dml-executor.d.ts.map +1 -1
- package/dist/src/runtime/emit/dml-executor.js +47 -0
- package/dist/src/runtime/emit/dml-executor.js.map +1 -1
- package/dist/src/runtime/emit/drop-assertion.d.ts.map +1 -1
- package/dist/src/runtime/emit/drop-assertion.js +2 -0
- package/dist/src/runtime/emit/drop-assertion.js.map +1 -1
- package/dist/src/runtime/emit/drop-table.d.ts.map +1 -1
- package/dist/src/runtime/emit/drop-table.js +2 -0
- package/dist/src/runtime/emit/drop-table.js.map +1 -1
- package/dist/src/runtime/emit/drop-view.d.ts.map +1 -1
- package/dist/src/runtime/emit/drop-view.js +2 -0
- package/dist/src/runtime/emit/drop-view.js.map +1 -1
- package/dist/src/runtime/emit/schema-declarative.d.ts.map +1 -1
- package/dist/src/runtime/emit/schema-declarative.js +4 -3
- package/dist/src/runtime/emit/schema-declarative.js.map +1 -1
- package/dist/src/runtime/emit/transaction.d.ts.map +1 -1
- package/dist/src/runtime/emit/transaction.js +16 -75
- package/dist/src/runtime/emit/transaction.js.map +1 -1
- package/dist/src/schema/manager.d.ts.map +1 -1
- package/dist/src/schema/manager.js +31 -0
- package/dist/src/schema/manager.js.map +1 -1
- package/dist/src/util/comparison.d.ts +5 -0
- package/dist/src/util/comparison.d.ts.map +1 -1
- package/dist/src/util/comparison.js +16 -0
- package/dist/src/util/comparison.js.map +1 -1
- package/dist/src/util/event-support.d.ts +15 -0
- package/dist/src/util/event-support.d.ts.map +1 -0
- package/dist/src/util/event-support.js +20 -0
- package/dist/src/util/event-support.js.map +1 -0
- package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/manager.js +5 -0
- package/dist/src/vtab/memory/layer/manager.js.map +1 -1
- package/package.json +16 -16
package/README.md
CHANGED
|
@@ -84,32 +84,39 @@ for await (const row of db.eval("select * from users")) {
|
|
|
84
84
|
### Reactive Patterns with Event Hooks
|
|
85
85
|
|
|
86
86
|
```typescript
|
|
87
|
-
import { Database
|
|
87
|
+
import { Database } from '@quereus/quereus';
|
|
88
88
|
|
|
89
89
|
const db = new Database();
|
|
90
|
-
const emitter = new DefaultVTableEventEmitter();
|
|
91
90
|
|
|
92
|
-
// Subscribe to changes
|
|
93
|
-
|
|
94
|
-
console.log(`${event.type} on ${event.tableName}
|
|
91
|
+
// Subscribe to data changes at the database level
|
|
92
|
+
db.onDataChange((event) => {
|
|
93
|
+
console.log(`${event.type} on ${event.tableName} (module: ${event.moduleName})`);
|
|
94
|
+
if (event.remote) {
|
|
95
|
+
console.log('Change came from remote sync');
|
|
96
|
+
}
|
|
95
97
|
if (event.type === 'update') {
|
|
96
98
|
console.log('Changed columns:', event.changedColumns);
|
|
97
99
|
}
|
|
98
100
|
});
|
|
99
101
|
|
|
100
|
-
//
|
|
101
|
-
db.
|
|
102
|
-
|
|
102
|
+
// Subscribe to schema changes
|
|
103
|
+
db.onSchemaChange((event) => {
|
|
104
|
+
console.log(`${event.type} ${event.objectType}: ${event.objectName}`);
|
|
105
|
+
});
|
|
103
106
|
|
|
104
107
|
// Events fire after commit
|
|
105
108
|
await db.exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)");
|
|
109
|
+
// Output: create table: users
|
|
110
|
+
|
|
106
111
|
await db.exec("INSERT INTO users VALUES (1, 'Alice')");
|
|
107
|
-
// Output: insert on users:
|
|
112
|
+
// Output: insert on users (module: memory)
|
|
108
113
|
```
|
|
109
114
|
|
|
115
|
+
The database-level event system aggregates events from all modules automatically. Events are batched within transactions and delivered only after successful commit.
|
|
116
|
+
|
|
110
117
|
SQL values use native JavaScript types (`string`, `number`, `bigint`, `Uint8Array`, `null`). Temporal types are ISO 8601 strings. Results stream as async iterators.
|
|
111
118
|
|
|
112
|
-
See the [Usage Guide](docs/usage.md) for complete API reference and [
|
|
119
|
+
See the [Usage Guide](docs/usage.md) for complete API reference and [Module Authoring Guide](docs/module-authoring.md) for event system details.
|
|
113
120
|
|
|
114
121
|
## Platform Support & Storage
|
|
115
122
|
|
|
@@ -205,7 +212,7 @@ See [Store Documentation](docs/store.md) for the storage architecture and custom
|
|
|
205
212
|
* [Type System](docs/types.md): Logical/physical types, temporal types, JSON, custom types
|
|
206
213
|
* [Functions](docs/functions.md): Built-in scalar, aggregate, window, and JSON functions
|
|
207
214
|
* [Memory Tables](docs/memory-table.md): Built-in MemoryTable module
|
|
208
|
-
* [
|
|
215
|
+
* [Module Authoring](docs/module-authoring.md): Virtual table module development and event system
|
|
209
216
|
* [Date/Time Handling](docs/datetime.md): Temporal parsing, functions, and ISO 8601 formats
|
|
210
217
|
* [Runtime](docs/runtime.md): Instruction-based execution and opcodes
|
|
211
218
|
* [Error Handling](docs/error.md): Error types and status codes
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global assertion evaluation for deferred constraint checking.
|
|
3
|
+
*
|
|
4
|
+
* This module handles the evaluation of CREATE ASSERTION constraints at transaction
|
|
5
|
+
* commit time. It optimizes assertion checking by:
|
|
6
|
+
* - Only evaluating assertions impacted by changed tables
|
|
7
|
+
* - Using row-specific filtering when possible to avoid full table scans
|
|
8
|
+
* - Injecting PK filters for parameterized per-row evaluation
|
|
9
|
+
*/
|
|
10
|
+
import { type SqlValue } from '../common/types.js';
|
|
11
|
+
import * as AST from '../parser/ast.js';
|
|
12
|
+
import { BlockNode } from '../planner/nodes/block.js';
|
|
13
|
+
import type { Database } from './database.js';
|
|
14
|
+
/**
|
|
15
|
+
* Interface for accessing Database internals needed by the assertion evaluator.
|
|
16
|
+
* This decouples the evaluator from the full Database class.
|
|
17
|
+
*/
|
|
18
|
+
export interface AssertionEvaluatorContext {
|
|
19
|
+
readonly schemaManager: Database['schemaManager'];
|
|
20
|
+
readonly optimizer: Database['optimizer'];
|
|
21
|
+
readonly options: Database['options'];
|
|
22
|
+
_buildPlan(statements: AST.Statement[]): BlockNode;
|
|
23
|
+
_findTable(tableName: string, schemaName?: string): ReturnType<Database['_findTable']>;
|
|
24
|
+
prepare(sql: string): ReturnType<Database['prepare']>;
|
|
25
|
+
getInstructionTracer(): ReturnType<Database['getInstructionTracer']>;
|
|
26
|
+
/** Get the set of changed base tables (lowercase qualified names) */
|
|
27
|
+
getChangedBaseTables(): Set<string>;
|
|
28
|
+
/** Get changed PK tuples for a specific base table */
|
|
29
|
+
getChangedKeyTuples(base: string): SqlValue[][];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Evaluates global assertions (CREATE ASSERTION) at transaction commit time.
|
|
33
|
+
*
|
|
34
|
+
* Assertions are evaluated only when the tables they reference have been modified.
|
|
35
|
+
* The evaluator uses constraint analysis to determine whether assertions can be
|
|
36
|
+
* checked per-row (more efficient) or require a full violation query.
|
|
37
|
+
*/
|
|
38
|
+
export declare class AssertionEvaluator {
|
|
39
|
+
private readonly ctx;
|
|
40
|
+
constructor(ctx: AssertionEvaluatorContext);
|
|
41
|
+
/**
|
|
42
|
+
* Run all global assertions that are impacted by changes in the current transaction.
|
|
43
|
+
* @throws QuereusError with CONSTRAINT status if any assertion is violated
|
|
44
|
+
*/
|
|
45
|
+
runGlobalAssertions(): Promise<void>;
|
|
46
|
+
private evaluateAssertion;
|
|
47
|
+
private executeViolationOnce;
|
|
48
|
+
private executeViolationPerChangedKeys;
|
|
49
|
+
private injectPkFilter;
|
|
50
|
+
private rewriteForPkFilter;
|
|
51
|
+
private tryWrapTableReference;
|
|
52
|
+
private collectTables;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=database-assertions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database-assertions.d.ts","sourceRoot":"","sources":["../../../src/core/database-assertions.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAG/D,OAAO,KAAK,GAAG,MAAM,kBAAkB,CAAC;AAIxC,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAQtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C;;;GAGG;AACH,MAAM,WAAW,yBAAyB;IACzC,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;IAClD,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC1C,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;IAEtC,UAAU,CAAC,UAAU,EAAE,GAAG,CAAC,SAAS,EAAE,GAAG,SAAS,CAAC;IACnD,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC;IACvF,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;IACtD,oBAAoB,IAAI,UAAU,CAAC,QAAQ,CAAC,sBAAsB,CAAC,CAAC,CAAC;IAErE,qEAAqE;IACrE,oBAAoB,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IACpC,sDAAsD;IACtD,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,EAAE,CAAC;CAChD;AAED;;;;;;GAMG;AACH,qBAAa,kBAAkB;IAClB,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,yBAAyB;IAE3D;;;OAGG;IACG,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;YAY5B,iBAAiB;YAgFjB,oBAAoB;YAapB,8BAA8B;IAmD5C,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,kBAAkB;IAe1B,OAAO,CAAC,qBAAqB;IAiD7B,OAAO,CAAC,aAAa;CAYrB"}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global assertion evaluation for deferred constraint checking.
|
|
3
|
+
*
|
|
4
|
+
* This module handles the evaluation of CREATE ASSERTION constraints at transaction
|
|
5
|
+
* commit time. It optimizes assertion checking by:
|
|
6
|
+
* - Only evaluating assertions impacted by changed tables
|
|
7
|
+
* - Using row-specific filtering when possible to avoid full table scans
|
|
8
|
+
* - Injecting PK filters for parameterized per-row evaluation
|
|
9
|
+
*/
|
|
10
|
+
import { QuereusError } from '../common/errors.js';
|
|
11
|
+
import { StatusCode } from '../common/types.js';
|
|
12
|
+
import { Parser } from '../parser/parser.js';
|
|
13
|
+
import { emitPlanNode } from '../runtime/emitters.js';
|
|
14
|
+
import { Scheduler } from '../runtime/scheduler.js';
|
|
15
|
+
import { BlockNode } from '../planner/nodes/block.js';
|
|
16
|
+
import { FilterNode } from '../planner/nodes/filter.js';
|
|
17
|
+
import { BinaryOpNode } from '../planner/nodes/scalar.js';
|
|
18
|
+
import { ParameterReferenceNode, ColumnReferenceNode, TableReferenceNode } from '../planner/nodes/reference.js';
|
|
19
|
+
import { EmissionContext } from '../runtime/emission-context.js';
|
|
20
|
+
import { isAsyncIterable } from '../runtime/utils.js';
|
|
21
|
+
import { analyzeRowSpecific } from '../planner/analysis/constraint-extractor.js';
|
|
22
|
+
/**
|
|
23
|
+
* Evaluates global assertions (CREATE ASSERTION) at transaction commit time.
|
|
24
|
+
*
|
|
25
|
+
* Assertions are evaluated only when the tables they reference have been modified.
|
|
26
|
+
* The evaluator uses constraint analysis to determine whether assertions can be
|
|
27
|
+
* checked per-row (more efficient) or require a full violation query.
|
|
28
|
+
*/
|
|
29
|
+
export class AssertionEvaluator {
|
|
30
|
+
ctx;
|
|
31
|
+
constructor(ctx) {
|
|
32
|
+
this.ctx = ctx;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Run all global assertions that are impacted by changes in the current transaction.
|
|
36
|
+
* @throws QuereusError with CONSTRAINT status if any assertion is violated
|
|
37
|
+
*/
|
|
38
|
+
async runGlobalAssertions() {
|
|
39
|
+
const assertions = this.ctx.schemaManager.getAllAssertions();
|
|
40
|
+
if (assertions.length === 0)
|
|
41
|
+
return;
|
|
42
|
+
const changedBases = this.ctx.getChangedBaseTables();
|
|
43
|
+
if (changedBases.size === 0)
|
|
44
|
+
return;
|
|
45
|
+
for (const assertion of assertions) {
|
|
46
|
+
await this.evaluateAssertion(assertion, changedBases);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async evaluateAssertion(assertion, changedBases) {
|
|
50
|
+
const parser = new Parser();
|
|
51
|
+
let ast;
|
|
52
|
+
try {
|
|
53
|
+
ast = parser.parse(assertion.violationSql);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
57
|
+
throw new QuereusError(`Failed to parse deferred assertion '${assertion.name}': ${error.message}`, StatusCode.INTERNAL, error);
|
|
58
|
+
}
|
|
59
|
+
const plan = this.ctx._buildPlan([ast]);
|
|
60
|
+
const analyzed = this.ctx.optimizer.optimizeForAnalysis(plan, this.ctx);
|
|
61
|
+
// Collect base tables and relationKeys in this plan
|
|
62
|
+
const relationKeyToBase = new Map();
|
|
63
|
+
const baseTablesInPlan = new Set();
|
|
64
|
+
this.collectTables(analyzed, relationKeyToBase, baseTablesInPlan);
|
|
65
|
+
// Determine impact: if assertion has no dependencies, treat as global and always impacted
|
|
66
|
+
const hasDeps = baseTablesInPlan.size > 0;
|
|
67
|
+
let impacted = !hasDeps;
|
|
68
|
+
if (hasDeps) {
|
|
69
|
+
for (const b of baseTablesInPlan) {
|
|
70
|
+
if (changedBases.has(b)) {
|
|
71
|
+
impacted = true;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (!impacted)
|
|
77
|
+
return;
|
|
78
|
+
// Classify instances as row/global
|
|
79
|
+
const classifications = analyzeRowSpecific(analyzed);
|
|
80
|
+
// If any changed base appears as a global instance, run full violation query once
|
|
81
|
+
let requiresGlobal = false;
|
|
82
|
+
for (const [relKey, klass] of classifications) {
|
|
83
|
+
if (klass === 'global') {
|
|
84
|
+
const base = relationKeyToBase.get(relKey);
|
|
85
|
+
if (base && changedBases.has(base)) {
|
|
86
|
+
requiresGlobal = true;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (requiresGlobal) {
|
|
92
|
+
await this.executeViolationOnce(assertion.name, assertion.violationSql);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
// Collect row-specific references that correspond to changed bases
|
|
96
|
+
const rowSpecificChanged = [];
|
|
97
|
+
for (const [relKey, klass] of classifications) {
|
|
98
|
+
if (klass !== 'row')
|
|
99
|
+
continue;
|
|
100
|
+
const base = relationKeyToBase.get(relKey);
|
|
101
|
+
if (base && changedBases.has(base)) {
|
|
102
|
+
rowSpecificChanged.push({ relKey, base });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (rowSpecificChanged.length === 0) {
|
|
106
|
+
// No row-specific changed refs (or no refs at all) → run once globally
|
|
107
|
+
await this.executeViolationOnce(assertion.name, assertion.violationSql);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
// Execute parameterized variants per changed key for each row-specific reference
|
|
111
|
+
for (const { relKey, base } of rowSpecificChanged) {
|
|
112
|
+
await this.executeViolationPerChangedKeys(assertion.name, analyzed, relKey, base);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async executeViolationOnce(assertionName, sql) {
|
|
116
|
+
const stmt = this.ctx.prepare(sql);
|
|
117
|
+
try {
|
|
118
|
+
// Use _iterateRowsRaw() to avoid transaction management - we're already inside
|
|
119
|
+
// the commit path and don't want to trigger nested commit/rollback behavior
|
|
120
|
+
for await (const _ of stmt._iterateRowsRaw()) {
|
|
121
|
+
throw new QuereusError(`Integrity assertion failed: ${assertionName}`, StatusCode.CONSTRAINT);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
await stmt.finalize();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async executeViolationPerChangedKeys(assertionName, analyzed, targetRelationKey, base) {
|
|
129
|
+
const changedKeyTuples = this.ctx.getChangedKeyTuples(base);
|
|
130
|
+
if (changedKeyTuples.length === 0)
|
|
131
|
+
return;
|
|
132
|
+
// Find PK indices for the base table
|
|
133
|
+
const [schemaName, tableName] = base.split('.');
|
|
134
|
+
const table = this.ctx._findTable(tableName, schemaName);
|
|
135
|
+
if (!table) {
|
|
136
|
+
throw new QuereusError(`Assertion references unknown table ${base}`, StatusCode.INTERNAL);
|
|
137
|
+
}
|
|
138
|
+
const pkIndices = table.primaryKeyDefinition.map(def => def.index);
|
|
139
|
+
// Prepare a rewritten plan with an injected Filter on the target relationKey
|
|
140
|
+
const rewritten = this.injectPkFilter(analyzed, targetRelationKey, pkIndices);
|
|
141
|
+
const optimizedPlan = this.ctx.optimizer.optimize(rewritten, this.ctx);
|
|
142
|
+
// Emit and execute for each changed PK tuple; stop on first violation row
|
|
143
|
+
const emissionContext = new EmissionContext(this.ctx);
|
|
144
|
+
const rootInstruction = emitPlanNode(optimizedPlan, emissionContext);
|
|
145
|
+
const scheduler = new Scheduler(rootInstruction);
|
|
146
|
+
for (const tuple of changedKeyTuples) {
|
|
147
|
+
const params = {};
|
|
148
|
+
for (let i = 0; i < pkIndices.length; i++) {
|
|
149
|
+
params[`pk${i}`] = tuple[i];
|
|
150
|
+
}
|
|
151
|
+
const runtimeCtx = {
|
|
152
|
+
db: this.ctx,
|
|
153
|
+
stmt: undefined,
|
|
154
|
+
params,
|
|
155
|
+
context: new Map(),
|
|
156
|
+
tableContexts: new Map(),
|
|
157
|
+
tracer: this.ctx.getInstructionTracer(),
|
|
158
|
+
enableMetrics: this.ctx.options.getBooleanOption('runtime_stats'),
|
|
159
|
+
};
|
|
160
|
+
const result = await scheduler.run(runtimeCtx);
|
|
161
|
+
if (isAsyncIterable(result)) {
|
|
162
|
+
for await (const _ of result) {
|
|
163
|
+
throw new QuereusError(`Integrity assertion failed: ${assertionName}`, StatusCode.CONSTRAINT);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
injectPkFilter(block, targetRelationKey, pkIndices) {
|
|
169
|
+
const newStatements = block.getChildren().map(stmt => this.rewriteForPkFilter(stmt, targetRelationKey, pkIndices));
|
|
170
|
+
if (newStatements.every((s, i) => s === block.getChildren()[i]))
|
|
171
|
+
return block;
|
|
172
|
+
return new BlockNode(block.scope, newStatements, block.parameters);
|
|
173
|
+
}
|
|
174
|
+
rewriteForPkFilter(node, targetRelationKey, pkIndices) {
|
|
175
|
+
// If this node is the target TableReference instance, wrap with a Filter
|
|
176
|
+
const maybe = this.tryWrapTableReference(node, targetRelationKey, pkIndices);
|
|
177
|
+
if (maybe)
|
|
178
|
+
return maybe;
|
|
179
|
+
const originalChildren = node.getChildren();
|
|
180
|
+
if (!originalChildren || originalChildren.length === 0)
|
|
181
|
+
return node;
|
|
182
|
+
const rewrittenChildren = originalChildren.map(child => this.rewriteForPkFilter(child, targetRelationKey, pkIndices));
|
|
183
|
+
const changed = rewrittenChildren.some((c, i) => c !== originalChildren[i]);
|
|
184
|
+
return changed ? node.withChildren(rewrittenChildren) : node;
|
|
185
|
+
}
|
|
186
|
+
tryWrapTableReference(node, targetRelationKey, pkIndices) {
|
|
187
|
+
if (!(node instanceof TableReferenceNode))
|
|
188
|
+
return null;
|
|
189
|
+
const tableSchema = node.tableSchema;
|
|
190
|
+
const schemaName = tableSchema.schemaName;
|
|
191
|
+
const tableName = tableSchema.name;
|
|
192
|
+
const relName = `${schemaName}.${tableName}`.toLowerCase();
|
|
193
|
+
const relKey = `${relName}#${node.id ?? 'unknown'}`;
|
|
194
|
+
if (relKey !== targetRelationKey)
|
|
195
|
+
return null;
|
|
196
|
+
// Build predicate: AND(col_pk_i = :pk{i}) for all PK columns
|
|
197
|
+
const relational = node;
|
|
198
|
+
const scope = relational.scope;
|
|
199
|
+
const attributes = relational.getAttributes();
|
|
200
|
+
const makeColumnRef = (colIndex) => {
|
|
201
|
+
const attr = attributes[colIndex];
|
|
202
|
+
const expr = { type: 'column', name: attr.name, table: tableName, schema: schemaName };
|
|
203
|
+
return new ColumnReferenceNode(scope, expr, attr.type, attr.id, colIndex);
|
|
204
|
+
};
|
|
205
|
+
const makeParamRef = (i, type) => {
|
|
206
|
+
const pexpr = { type: 'parameter', name: `pk${i}` };
|
|
207
|
+
return new ParameterReferenceNode(scope, pexpr, `pk${i}`, type);
|
|
208
|
+
};
|
|
209
|
+
let predicate = null;
|
|
210
|
+
for (let i = 0; i < pkIndices.length; i++) {
|
|
211
|
+
const colIdx = pkIndices[i];
|
|
212
|
+
const left = makeColumnRef(colIdx);
|
|
213
|
+
const right = makeParamRef(i, attributes[colIdx].type);
|
|
214
|
+
const bexpr = { type: 'binary', operator: '=', left: left.expression, right: right.expression };
|
|
215
|
+
const eqNode = new BinaryOpNode(scope, bexpr, left, right);
|
|
216
|
+
predicate = predicate
|
|
217
|
+
? new BinaryOpNode(scope, { type: 'binary', operator: 'AND', left: predicate.expression, right: eqNode.expression }, predicate, eqNode)
|
|
218
|
+
: eqNode;
|
|
219
|
+
}
|
|
220
|
+
if (!predicate)
|
|
221
|
+
return null;
|
|
222
|
+
return new FilterNode(scope, relational, predicate);
|
|
223
|
+
}
|
|
224
|
+
collectTables(node, relToBase, bases) {
|
|
225
|
+
for (const child of node.getChildren()) {
|
|
226
|
+
this.collectTables(child, relToBase, bases);
|
|
227
|
+
}
|
|
228
|
+
if (node instanceof TableReferenceNode) {
|
|
229
|
+
const schema = node.tableSchema;
|
|
230
|
+
const baseName = `${schema.schemaName}.${schema.name}`.toLowerCase();
|
|
231
|
+
bases.add(baseName);
|
|
232
|
+
const relKey = `${baseName}#${node.id ?? 'unknown'}`;
|
|
233
|
+
relToBase.set(relKey, baseName);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=database-assertions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database-assertions.js","sourceRoot":"","sources":["../../../src/core/database-assertions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database-level event system for unified reactivity.
|
|
3
|
+
*
|
|
4
|
+
* This module provides a centralized event aggregator that collects and broadcasts
|
|
5
|
+
* data and schema change events from all virtual table modules. Events are batched
|
|
6
|
+
* within transactions and emitted after successful commit.
|
|
7
|
+
*
|
|
8
|
+
* Modules that implement their own event emission (detected via getEventEmitter())
|
|
9
|
+
* will have their events forwarded to the database level. For modules without
|
|
10
|
+
* native event support, the engine automatically emits events for local operations.
|
|
11
|
+
*/
|
|
12
|
+
import type { Row, SqlValue } from '../common/types.js';
|
|
13
|
+
import type { VTableDataChangeEvent, VTableSchemaChangeEvent, VTableEventEmitter } from '../vtab/events.js';
|
|
14
|
+
/**
|
|
15
|
+
* Data change event emitted at the database level.
|
|
16
|
+
* Extends VTableDataChangeEvent with module identification.
|
|
17
|
+
*/
|
|
18
|
+
export interface DatabaseDataChangeEvent {
|
|
19
|
+
/** The type of mutation operation */
|
|
20
|
+
type: 'insert' | 'update' | 'delete';
|
|
21
|
+
/** The module that raised this event */
|
|
22
|
+
moduleName: string;
|
|
23
|
+
/** Schema name containing the table */
|
|
24
|
+
schemaName: string;
|
|
25
|
+
/** Table name */
|
|
26
|
+
tableName: string;
|
|
27
|
+
/** Primary key values */
|
|
28
|
+
key?: SqlValue[];
|
|
29
|
+
/** Previous row data (for update/delete) */
|
|
30
|
+
oldRow?: Row;
|
|
31
|
+
/** New row data (for insert/update) */
|
|
32
|
+
newRow?: Row;
|
|
33
|
+
/** Column names that changed (for updates) */
|
|
34
|
+
changedColumns?: string[];
|
|
35
|
+
/** True if event originated from sync/remote source, false for local changes */
|
|
36
|
+
remote: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Schema change event emitted at the database level.
|
|
40
|
+
* Extends VTableSchemaChangeEvent with module identification.
|
|
41
|
+
*/
|
|
42
|
+
export interface DatabaseSchemaChangeEvent {
|
|
43
|
+
/** The type of schema operation */
|
|
44
|
+
type: 'create' | 'alter' | 'drop';
|
|
45
|
+
/** The type of object being modified */
|
|
46
|
+
objectType: 'table' | 'index' | 'column';
|
|
47
|
+
/** The module that raised this event */
|
|
48
|
+
moduleName: string;
|
|
49
|
+
/** Schema name */
|
|
50
|
+
schemaName: string;
|
|
51
|
+
/** Object name (table name for table/column, index name for index) */
|
|
52
|
+
objectName: string;
|
|
53
|
+
/** Column name (for column operations) */
|
|
54
|
+
columnName?: string;
|
|
55
|
+
/** Old column name (for column rename) */
|
|
56
|
+
oldColumnName?: string;
|
|
57
|
+
/** DDL statement if available */
|
|
58
|
+
ddl?: string;
|
|
59
|
+
/** True if event originated from sync/remote source, false for local changes */
|
|
60
|
+
remote: boolean;
|
|
61
|
+
}
|
|
62
|
+
export type DatabaseDataChangeListener = (event: DatabaseDataChangeEvent) => void;
|
|
63
|
+
export type DatabaseSchemaChangeListener = (event: DatabaseSchemaChangeEvent) => void;
|
|
64
|
+
/**
|
|
65
|
+
* Options for subscribing to data change events.
|
|
66
|
+
* Reserved fields for future filtering capabilities, plus pass-through for module-specific options.
|
|
67
|
+
*/
|
|
68
|
+
export interface DataChangeSubscriptionOptions {
|
|
69
|
+
/** Module-specific options passed through to modules */
|
|
70
|
+
[key: string]: unknown;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Options for subscribing to schema change events.
|
|
74
|
+
*/
|
|
75
|
+
export interface SchemaChangeSubscriptionOptions {
|
|
76
|
+
/** Module-specific options passed through to modules */
|
|
77
|
+
[key: string]: unknown;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Central event emitter for database-level reactivity.
|
|
81
|
+
*
|
|
82
|
+
* Aggregates events from all virtual table modules and broadcasts them to
|
|
83
|
+
* registered listeners. Handles transaction batching - events are collected
|
|
84
|
+
* during a transaction and emitted only after successful commit.
|
|
85
|
+
*
|
|
86
|
+
* Supports savepoint semantics: events within a savepoint are tracked separately
|
|
87
|
+
* and can be discarded on ROLLBACK TO SAVEPOINT or merged on RELEASE.
|
|
88
|
+
*/
|
|
89
|
+
export declare class DatabaseEventEmitter {
|
|
90
|
+
private dataListeners;
|
|
91
|
+
private schemaListeners;
|
|
92
|
+
/** Batched events waiting for commit (base transaction level) */
|
|
93
|
+
private batchedDataEvents;
|
|
94
|
+
private batchedSchemaEvents;
|
|
95
|
+
/** Savepoint layers for event batching - each layer captures events since that savepoint */
|
|
96
|
+
private dataEventLayers;
|
|
97
|
+
private schemaEventLayers;
|
|
98
|
+
/** Whether we're currently in a transaction (batching mode) */
|
|
99
|
+
private isBatching;
|
|
100
|
+
/** Map of module emitters we've subscribed to, for cleanup */
|
|
101
|
+
private moduleSubscriptions;
|
|
102
|
+
/**
|
|
103
|
+
* Subscribe to data change events from all modules.
|
|
104
|
+
* @param listener Callback invoked for each data change event
|
|
105
|
+
* @param _options Reserved for future filtering options
|
|
106
|
+
* @returns Unsubscribe function
|
|
107
|
+
*/
|
|
108
|
+
onDataChange(listener: DatabaseDataChangeListener, _options?: DataChangeSubscriptionOptions): () => void;
|
|
109
|
+
/**
|
|
110
|
+
* Subscribe to schema change events from all modules.
|
|
111
|
+
* @param listener Callback invoked for each schema change event
|
|
112
|
+
* @param _options Reserved for future filtering options
|
|
113
|
+
* @returns Unsubscribe function
|
|
114
|
+
*/
|
|
115
|
+
onSchemaChange(listener: DatabaseSchemaChangeListener, _options?: SchemaChangeSubscriptionOptions): () => void;
|
|
116
|
+
/**
|
|
117
|
+
* Check if there are any data change listeners registered.
|
|
118
|
+
*/
|
|
119
|
+
hasDataListeners(): boolean;
|
|
120
|
+
/**
|
|
121
|
+
* Check if there are any schema change listeners registered.
|
|
122
|
+
*/
|
|
123
|
+
hasSchemaListeners(): boolean;
|
|
124
|
+
/**
|
|
125
|
+
* Hook a module's event emitter to forward events to the database level.
|
|
126
|
+
* Called when a module with native event support is detected.
|
|
127
|
+
*
|
|
128
|
+
* @param moduleName The name of the module
|
|
129
|
+
* @param emitter The module's event emitter
|
|
130
|
+
*/
|
|
131
|
+
hookModuleEmitter(moduleName: string, emitter: VTableEventEmitter): void;
|
|
132
|
+
/**
|
|
133
|
+
* Unhook a module's event emitter.
|
|
134
|
+
* Called when a module is unregistered.
|
|
135
|
+
*
|
|
136
|
+
* @param moduleName The name of the module
|
|
137
|
+
*/
|
|
138
|
+
unhookModuleEmitter(moduleName: string): void;
|
|
139
|
+
/**
|
|
140
|
+
* Get the active data event store (top layer or base).
|
|
141
|
+
*/
|
|
142
|
+
private getActiveDataStore;
|
|
143
|
+
/**
|
|
144
|
+
* Get the active schema event store (top layer or base).
|
|
145
|
+
*/
|
|
146
|
+
private getActiveSchemaStore;
|
|
147
|
+
/**
|
|
148
|
+
* Handle a data change event from a module.
|
|
149
|
+
* If batching, queue the event; otherwise emit immediately.
|
|
150
|
+
*/
|
|
151
|
+
private handleModuleDataEvent;
|
|
152
|
+
/**
|
|
153
|
+
* Handle a schema change event from a module.
|
|
154
|
+
* Schema events are typically not batched (DDL is usually auto-committed),
|
|
155
|
+
* but we support batching for consistency.
|
|
156
|
+
*/
|
|
157
|
+
private handleModuleSchemaEvent;
|
|
158
|
+
/**
|
|
159
|
+
* Emit a data change event for a module that doesn't have native event support.
|
|
160
|
+
* Called by the engine after successful DML operations.
|
|
161
|
+
*
|
|
162
|
+
* @param moduleName The module name
|
|
163
|
+
* @param event The event to emit (will be converted to DatabaseDataChangeEvent)
|
|
164
|
+
*/
|
|
165
|
+
emitAutoDataEvent(moduleName: string, event: VTableDataChangeEvent): void;
|
|
166
|
+
/**
|
|
167
|
+
* Emit a schema change event for a module that doesn't have native event support.
|
|
168
|
+
* Called by the engine after successful DDL operations.
|
|
169
|
+
*
|
|
170
|
+
* @param moduleName The module name
|
|
171
|
+
* @param event The event to emit
|
|
172
|
+
*/
|
|
173
|
+
emitAutoSchemaEvent(moduleName: string, event: VTableSchemaChangeEvent): void;
|
|
174
|
+
/**
|
|
175
|
+
* Emit a data event to all listeners.
|
|
176
|
+
*/
|
|
177
|
+
private emitDataEvent;
|
|
178
|
+
/**
|
|
179
|
+
* Emit a schema event to all listeners.
|
|
180
|
+
*/
|
|
181
|
+
private emitSchemaEvent;
|
|
182
|
+
/**
|
|
183
|
+
* Start batching events (called at transaction begin).
|
|
184
|
+
*/
|
|
185
|
+
startBatch(): void;
|
|
186
|
+
/**
|
|
187
|
+
* Flush all batched events to listeners (called after successful commit).
|
|
188
|
+
* Collects events from all layers (base + savepoint layers) and emits them.
|
|
189
|
+
*/
|
|
190
|
+
flushBatch(): void;
|
|
191
|
+
/**
|
|
192
|
+
* Discard all batched events (called on rollback).
|
|
193
|
+
*/
|
|
194
|
+
discardBatch(): void;
|
|
195
|
+
/**
|
|
196
|
+
* Begin a new savepoint layer for event batching.
|
|
197
|
+
* Events after this point will be captured in the new layer.
|
|
198
|
+
*/
|
|
199
|
+
beginSavepointLayer(): void;
|
|
200
|
+
/**
|
|
201
|
+
* Rollback the current savepoint layer, discarding its events.
|
|
202
|
+
* Called on ROLLBACK TO SAVEPOINT.
|
|
203
|
+
*/
|
|
204
|
+
rollbackSavepointLayer(): void;
|
|
205
|
+
/**
|
|
206
|
+
* Release the current savepoint layer, merging its events into the parent layer.
|
|
207
|
+
* Called on RELEASE SAVEPOINT.
|
|
208
|
+
*/
|
|
209
|
+
releaseSavepointLayer(): void;
|
|
210
|
+
/**
|
|
211
|
+
* Remove all listeners and unhook all modules.
|
|
212
|
+
*/
|
|
213
|
+
removeAllListeners(): void;
|
|
214
|
+
/**
|
|
215
|
+
* Check if currently batching events.
|
|
216
|
+
*/
|
|
217
|
+
isBatchingEvents(): boolean;
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=database-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database-events.d.ts","sourceRoot":"","sources":["../../../src/core/database-events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAI5G;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACvC,qCAAqC;IACrC,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACrC,wCAAwC;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,yBAAyB;IACzB,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC;IACjB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,uCAAuC;IACvC,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,8CAA8C;IAC9C,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,gFAAgF;IAChF,MAAM,EAAE,OAAO,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,yBAAyB;IACzC,mCAAmC;IACnC,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;IAClC,wCAAwC;IACxC,UAAU,EAAE,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC;IACzC,wCAAwC;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gFAAgF;IAChF,MAAM,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,MAAM,0BAA0B,GAAG,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,CAAC;AAClF,MAAM,MAAM,4BAA4B,GAAG,CAAC,KAAK,EAAE,yBAAyB,KAAK,IAAI,CAAC;AAEtF;;;GAGG;AACH,MAAM,WAAW,6BAA6B;IAQ7C,wDAAwD;IACxD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAK/C,wDAAwD;IACxD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAmBD;;;;;;;;;GASG;AACH,qBAAa,oBAAoB;IAChC,OAAO,CAAC,aAAa,CAAyC;IAC9D,OAAO,CAAC,eAAe,CAA2C;IAElE,iEAAiE;IACjE,OAAO,CAAC,iBAAiB,CAA0B;IACnD,OAAO,CAAC,mBAAmB,CAA4B;IAEvD,4FAA4F;IAC5F,OAAO,CAAC,eAAe,CAA4B;IACnD,OAAO,CAAC,iBAAiB,CAA8B;IAEvD,+DAA+D;IAC/D,OAAO,CAAC,UAAU,CAAS;IAE3B,8DAA8D;IAC9D,OAAO,CAAC,mBAAmB,CAA2E;IAEtG;;;;;OAKG;IACH,YAAY,CACX,QAAQ,EAAE,0BAA0B,EACpC,QAAQ,CAAC,EAAE,6BAA6B,GACtC,MAAM,IAAI;IASb;;;;;OAKG;IACH,cAAc,CACb,QAAQ,EAAE,4BAA4B,EACtC,QAAQ,CAAC,EAAE,+BAA+B,GACxC,MAAM,IAAI;IASb;;OAEG;IACH,gBAAgB,IAAI,OAAO;IAI3B;;OAEG;IACH,kBAAkB,IAAI,OAAO;IAI7B;;;;;;OAMG;IACH,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,GAAG,IAAI;IA0BxE;;;;;OAKG;IACH,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAU7C;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAM1B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAM5B;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAS7B;;;;OAIG;IACH,OAAO,CAAC,uBAAuB;IAS/B;;;;;;OAMG;IACH,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB,GAAG,IAAI;IASzE;;;;;;OAMG;IACH,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,uBAAuB,GAAG,IAAI;IAQ7E;;OAEG;IACH,OAAO,CAAC,aAAa;IA2BrB;;OAEG;IACH,OAAO,CAAC,eAAe;IA2BvB;;OAEG;IACH,UAAU,IAAI,IAAI;IASlB;;;OAGG;IACH,UAAU,IAAI,IAAI;IAiClB;;OAEG;IACH,YAAY,IAAI,IAAI;IAWpB;;;OAGG;IACH,mBAAmB,IAAI,IAAI;IAM3B;;;OAGG;IACH,sBAAsB,IAAI,IAAI;IAO9B;;;OAGG;IACH,qBAAqB,IAAI,IAAI;IAuB7B;;OAEG;IACH,kBAAkB,IAAI,IAAI;IAmB1B;;OAEG;IACH,gBAAgB,IAAI,OAAO;CAG3B"}
|