@fadhilp/stateql 0.11.1 → 0.13.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 +110 -0
- package/dist/src/adapters.d.ts +2 -0
- package/dist/src/adapters.js +95 -2
- package/dist/src/sql.d.ts +6 -1
- package/dist/src/sql.js +1086 -95
- package/dist/src/sqlite-process.js +16 -0
- package/dist/src/stateql.js +40 -5
- package/package.json +2 -1
|
@@ -83,6 +83,22 @@ function execute(request) {
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
|
+
case "writeAutocommit": {
|
|
87
|
+
if (readOnly)
|
|
88
|
+
throw new SQLiteBatchError("Connection is read-only.", false);
|
|
89
|
+
const [sql, params] = request.args;
|
|
90
|
+
if ((Array.isArray(params) && params.length > 0) ||
|
|
91
|
+
(!Array.isArray(params) && Object.keys(params).length > 0)) {
|
|
92
|
+
throw new SQLiteBatchError("SQLite maintenance statements do not accept parameters.", false);
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
database.exec(sql);
|
|
96
|
+
return { affectedRows: 0 };
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
throw new SQLiteBatchError(errorText(error), true);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
86
102
|
case "writeBatch": {
|
|
87
103
|
if (readOnly)
|
|
88
104
|
throw new Error("Connection is read-only.");
|
package/dist/src/stateql.js
CHANGED
|
@@ -672,7 +672,17 @@ export class StateQL {
|
|
|
672
672
|
if (!analysis.read) {
|
|
673
673
|
throw new StateQLError("INVALID_SQL", "query accepts read statements only; use exec for writes.");
|
|
674
674
|
}
|
|
675
|
+
const cacheMode = options.cache ?? "auto";
|
|
676
|
+
if (!analysis.cacheable && cacheMode === "require") {
|
|
677
|
+
throw new StateQLError("CACHE_MISS", "This statement is not cacheable.", {
|
|
678
|
+
retryable: true,
|
|
679
|
+
suggestedAction: "Run with --cache auto or --cache bypass.",
|
|
680
|
+
});
|
|
681
|
+
}
|
|
675
682
|
const parameters = options.params ?? [];
|
|
683
|
+
if (analysis.requiresAutocommit && sqlParametersLength(parameters) > 0) {
|
|
684
|
+
throw new StateQLError("INVALID_SQL", "Autocommit diagnostic statements do not accept StateQL parameters.");
|
|
685
|
+
}
|
|
676
686
|
const context = this.executionContext(options);
|
|
677
687
|
const adapterSource = await this.resolveConnectionSource(connection, session, "query", "read", context);
|
|
678
688
|
const adapter = await this.openAdapter(connection, context, adapterSource);
|
|
@@ -689,8 +699,8 @@ export class StateQL {
|
|
|
689
699
|
stateVersion,
|
|
690
700
|
});
|
|
691
701
|
const cached = this.store.findResult(fingerprint);
|
|
692
|
-
|
|
693
|
-
|
|
702
|
+
if (analysis.cacheable &&
|
|
703
|
+
cacheMode !== "bypass" &&
|
|
694
704
|
cached &&
|
|
695
705
|
cached.row_count <= this.maxResultRows &&
|
|
696
706
|
this.cacheValid(cached, stateVersion, stateSignature)) {
|
|
@@ -709,7 +719,15 @@ export class StateQL {
|
|
|
709
719
|
suggestedAction: "Run with --cache auto or --cache bypass.",
|
|
710
720
|
});
|
|
711
721
|
}
|
|
712
|
-
const
|
|
722
|
+
const executionSql = analysis.wrapForLimit
|
|
723
|
+
? boundedReadSql(analysis.limitSql ?? sql, this.maxResultRows + 1)
|
|
724
|
+
: sql;
|
|
725
|
+
if (analysis.requiresAutocommit && !adapter.readAutocommit) {
|
|
726
|
+
throw new StateQLError("UNSUPPORTED_DRIVER", `${analysis.statementType.toUpperCase()} requires adapter autocommit reads.`);
|
|
727
|
+
}
|
|
728
|
+
const result = analysis.requiresAutocommit
|
|
729
|
+
? await adapter.readAutocommit(executionSql, parameters)
|
|
730
|
+
: await adapter.read(executionSql, parameters);
|
|
713
731
|
if (result.rows.length > this.maxResultRows) {
|
|
714
732
|
throw new StateQLError("OUTPUT_LIMIT_EXCEEDED", `Query exceeds the ${this.maxResultRows}-row materialization limit.`, { suggestedAction: "Add a narrower WHERE clause or LIMIT." });
|
|
715
733
|
}
|
|
@@ -2200,12 +2218,19 @@ export class StateQL {
|
|
|
2200
2218
|
throw new StateQLError("INVALID_COMMAND", "Idempotency key cannot be empty.");
|
|
2201
2219
|
}
|
|
2202
2220
|
const parameters = options.params ?? [];
|
|
2221
|
+
if (analysis.requiresAutocommit &&
|
|
2222
|
+
(options.expectedRows !== undefined || sqlParametersLength(parameters) > 0)) {
|
|
2223
|
+
throw new StateQLError("INVALID_SQL", "Autocommit maintenance statements do not accept StateQL parameters or row-count preconditions.");
|
|
2224
|
+
}
|
|
2225
|
+
const transactionId = session.active_transaction_id ?? undefined;
|
|
2226
|
+
if (analysis.requiresAutocommit && transactionId) {
|
|
2227
|
+
throw new StateQLError("TRANSACTION_FAILED", `${analysis.statementType.toUpperCase()} cannot be staged in a transaction.`, { suggestedAction: "Rollback or commit the staged transaction, then run the maintenance statement separately." });
|
|
2228
|
+
}
|
|
2203
2229
|
const fingerprint = hash({
|
|
2204
2230
|
sql: analysis.normalized,
|
|
2205
2231
|
parameters,
|
|
2206
2232
|
database: databaseIdentity(connection),
|
|
2207
2233
|
});
|
|
2208
|
-
const transactionId = session.active_transaction_id ?? undefined;
|
|
2209
2234
|
if (transactionId) {
|
|
2210
2235
|
const transaction = this.store.getTransaction(transactionId);
|
|
2211
2236
|
if (!transaction ||
|
|
@@ -2305,7 +2330,12 @@ export class StateQL {
|
|
|
2305
2330
|
});
|
|
2306
2331
|
}
|
|
2307
2332
|
try {
|
|
2308
|
-
|
|
2333
|
+
if (analysis.requiresAutocommit && !adapter.writeAutocommit) {
|
|
2334
|
+
throw new AdapterWriteError(`${analysis.statementType.toUpperCase()} requires adapter autocommit execution.`, false);
|
|
2335
|
+
}
|
|
2336
|
+
const write = analysis.requiresAutocommit
|
|
2337
|
+
? await adapter.writeAutocommit(sql, parameters)
|
|
2338
|
+
: await adapter.write(sql, parameters, options.expectedRows);
|
|
2309
2339
|
try {
|
|
2310
2340
|
const finalized = planClaim
|
|
2311
2341
|
? this.store.finishPlannedOperation({
|
|
@@ -3083,6 +3113,11 @@ function markTransactionOutcomeUnknown(store, transactionId, sessionId, actorId)
|
|
|
3083
3113
|
// A stale committing transaction is recovered as unknown after five minutes.
|
|
3084
3114
|
}
|
|
3085
3115
|
}
|
|
3116
|
+
function sqlParametersLength(parameters) {
|
|
3117
|
+
return Array.isArray(parameters)
|
|
3118
|
+
? parameters.length
|
|
3119
|
+
: Object.keys(parameters).length;
|
|
3120
|
+
}
|
|
3086
3121
|
function boundedReadSql(sql, limit) {
|
|
3087
3122
|
const statement = sql.trim().replace(/;\s*$/, "");
|
|
3088
3123
|
return `SELECT * FROM (${statement}) AS _stateql_bounded LIMIT ${limit}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fadhilp/stateql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Stateful, agent-oriented database CLI for safe result reuse",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"files": [
|
|
22
22
|
"dist/src",
|
|
23
23
|
"README.md",
|
|
24
|
+
"SQL_COMMAND_ROADMAP.md",
|
|
24
25
|
"LICENSE"
|
|
25
26
|
],
|
|
26
27
|
"scripts": {
|