@fadhilp/stateql 0.12.0 → 0.13.1

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.
@@ -32,6 +32,7 @@ export interface Adapter {
32
32
  readonly confidence: StateConfidence;
33
33
  ping(): Promise<void>;
34
34
  read(sql: string, params: SqlParameters): Promise<ReadResult>;
35
+ readAutocommit?(sql: string, params: SqlParameters): Promise<ReadResult>;
35
36
  write(sql: string, params: SqlParameters, expectedRows?: 1): Promise<WriteResult>;
36
37
  writeAutocommit?(sql: string, params: SqlParameters): Promise<WriteResult>;
37
38
  writeBatch(operations: BatchWriteOperation[], isolation: string): Promise<WriteResult[]>;
@@ -51,6 +51,7 @@ export async function createAdapter(connection, context, input) {
51
51
  throw new StateQLError("UNSUPPORTED_DRIVER", "Redis uses the native Redis adapter.");
52
52
  }
53
53
  }
54
+ const SQLITE_AUTOCOMMIT_STATEMENTS = new Set(["vacuum", "analyze", "reindex"]);
54
55
  class SQLiteAdapter {
55
56
  source;
56
57
  readOnly;
@@ -101,7 +102,14 @@ class SQLiteAdapter {
101
102
  async write(sql, params, expectedRows) {
102
103
  return this.call("write", [sql, params, expectedRows], true, false);
103
104
  }
105
+ async writeAutocommit(sql, params) {
106
+ return this.call("writeAutocommit", [sql, params], true, false);
107
+ }
104
108
  async writeBatch(operations, isolation) {
109
+ const unsupported = operations.find((operation) => SQLITE_AUTOCOMMIT_STATEMENTS.has(operation.statement_type));
110
+ if (unsupported) {
111
+ throw new BatchWriteError(`SQLite transactions cannot include ${unsupported.statement_type.toUpperCase()} maintenance statements.`, false);
112
+ }
105
113
  return this.call("writeBatch", [operations, isolation], true, true);
106
114
  }
107
115
  async signature() {
@@ -590,6 +598,16 @@ class MySqlAdapter {
590
598
  throw error;
591
599
  }
592
600
  }
601
+ async readAutocommit(sql, params) {
602
+ const [result, fields] = await this.query(sql, mysqlParams(params), false, false);
603
+ return {
604
+ rows: toJsonSafe(mysqlRows(result)),
605
+ columns: fields.map((field) => ({
606
+ name: field.name,
607
+ type: mysqlFieldType(field),
608
+ })),
609
+ };
610
+ }
593
611
  async write(sql, params, expectedRows) {
594
612
  if (this.readOnly)
595
613
  throw new Error("Connection is read-only.");
@@ -629,6 +647,35 @@ class MySqlAdapter {
629
647
  throw new AdapterWriteError(errorText(error), true);
630
648
  }
631
649
  }
650
+ async writeAutocommit(sql, params) {
651
+ if (this.readOnly)
652
+ throw new AdapterWriteError("Connection is read-only.", false);
653
+ let values;
654
+ try {
655
+ values = mysqlParams(params);
656
+ await this.connect();
657
+ }
658
+ catch (error) {
659
+ if (error instanceof AdapterExecutionError)
660
+ throw error;
661
+ throw new AdapterWriteError(errorText(error), false);
662
+ }
663
+ try {
664
+ const [result] = await this.runQuery(sql, values, true, false);
665
+ const maintenanceError = mysqlMaintenanceError(result);
666
+ if (maintenanceError)
667
+ throw new AdapterWriteError(maintenanceError, false);
668
+ return {
669
+ affectedRows: Array.isArray(result) ? 0 : mysqlAffectedRows(result),
670
+ };
671
+ }
672
+ catch (error) {
673
+ if (error instanceof AdapterExecutionError || error instanceof AdapterWriteError) {
674
+ throw error;
675
+ }
676
+ throw new AdapterWriteError(errorText(error), true);
677
+ }
678
+ }
632
679
  async writeBatch(operations, isolation) {
633
680
  if (this.readOnly)
634
681
  throw new Error("Connection is read-only.");
@@ -901,6 +948,7 @@ const MYSQL_ISOLATION_LEVELS = new Set([
901
948
  const MYSQL_TRANSACTIONAL_STATEMENTS = new Set([
902
949
  "delete",
903
950
  "insert",
951
+ "upsert",
904
952
  "replace",
905
953
  "update",
906
954
  ]);
@@ -927,6 +975,18 @@ function mysqlRows(result) {
927
975
  }
928
976
  return result;
929
977
  }
978
+ function mysqlMaintenanceError(result) {
979
+ if (!Array.isArray(result))
980
+ return undefined;
981
+ for (const row of result) {
982
+ const typeEntry = Object.entries(row).find(([key]) => key.toLowerCase() === "msg_type");
983
+ if (String(typeEntry?.[1] ?? "").toLowerCase() !== "error")
984
+ continue;
985
+ const textEntry = Object.entries(row).find(([key]) => key.toLowerCase() === "msg_text");
986
+ return String(textEntry?.[1] ?? "MySQL maintenance failed.");
987
+ }
988
+ return undefined;
989
+ }
930
990
  function mysqlAffectedRows(result) {
931
991
  if (Array.isArray(result) || !("affectedRows" in result)) {
932
992
  throw new Error("MySQL statement did not return a write result.");
package/dist/src/sql.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { AST } from "node-sql-parser";
2
2
  import type { Driver, SqlDriver } from "./types.js";
3
- export type StatementType = "select" | "insert" | "replace" | "update" | "delete" | "create" | "alter" | "drop" | "truncate" | "explain" | "vacuum" | "analyze" | "reindex" | "cluster";
3
+ export type StatementType = "select" | "insert" | "upsert" | "replace" | "update" | "delete" | "create" | "alter" | "drop" | "truncate" | "values" | "show" | "explain" | "vacuum" | "analyze" | "reindex" | "optimize" | "check" | "cluster";
4
4
  export interface SqlAnalysis {
5
5
  ast: AST;
6
6
  normalized: string;
@@ -12,6 +12,8 @@ export interface SqlAnalysis {
12
12
  wrapForLimit: boolean;
13
13
  cacheable: boolean;
14
14
  requiresAutocommit: boolean;
15
+ /** Analysis-only SQL with trailing trivia removed when limit wrapping needs it. */
16
+ limitSql?: string;
15
17
  }
16
18
  export declare function analyzeSql(sql: string, driver: SqlDriver): SqlAnalysis;
17
19
  /** @internal Compatibility for existing connection records during Mongo rollout. */