rado 1.0.5 → 1.0.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.
@@ -8,6 +8,7 @@ import { QueryBatch } from './Query.js';
8
8
  import { Resolver } from './Resolver.js';
9
9
  import type { Table } from './Table.js';
10
10
  export declare class Database<Meta extends QueryMeta = Either> extends Builder<Meta> implements HasResolver<Meta> {
11
+ #private;
11
12
  driver: Driver;
12
13
  dialect: Dialect;
13
14
  diff: Diff;
@@ -26,17 +27,20 @@ export declare class Database<Meta extends QueryMeta = Either> extends Builder<M
26
27
  transaction<T>(this: Database<Async>, run: (tx: Transaction<Meta>) => Promise<T>, options?: TransactionOptions[Meta['dialect']]): Promise<T>;
27
28
  transaction<T>(run: (tx: Transaction<Meta>) => T | Promise<T>, options?: TransactionOptions[Meta['dialect']]): Deliver<Meta, T>;
28
29
  }
30
+ export interface TransactionUniversalOptions {
31
+ async?: boolean;
32
+ }
29
33
  export interface TransactionOptions {
30
- universal: never;
31
- sqlite: {
34
+ universal: TransactionUniversalOptions;
35
+ sqlite: TransactionUniversalOptions & {
32
36
  behavior?: 'deferred' | 'immediate' | 'exclusive';
33
37
  };
34
- postgres: {
38
+ postgres: TransactionUniversalOptions & {
35
39
  isolationLevel?: 'read uncommitted' | 'read committed' | 'repeatable read' | 'serializable';
36
40
  accessMode?: 'read only' | 'read write';
37
41
  deferrable?: boolean;
38
42
  };
39
- mysql: {
43
+ mysql: TransactionUniversalOptions & {
40
44
  isolationLevel?: 'read uncommitted' | 'read committed' | 'repeatable read' | 'serializable';
41
45
  accessMode?: 'read only' | 'read write';
42
46
  withConsistentSnapshot?: boolean;
@@ -64,11 +64,15 @@ var Database = class extends Builder {
64
64
  throw new Error("Query has parameters");
65
65
  return this.driver.exec(emitter.sql);
66
66
  }
67
+ #transactionLock;
67
68
  transaction(run, options = {}) {
68
- return this.driver.transaction((inner) => {
69
- const tx = new Transaction(inner, this.dialect, this.diff);
70
- return run(tx);
71
- }, options);
69
+ return this.driver.transaction(
70
+ (inner) => {
71
+ const tx = new Transaction(inner, this.dialect, this.diff);
72
+ return run(tx);
73
+ },
74
+ { async: run.constructor.name === "AsyncFunction", ...options }
75
+ );
72
76
  }
73
77
  };
74
78
  var Rollback = class extends Error {
@@ -1,3 +1,4 @@
1
+ import type { TransactionUniversalOptions } from './Database.js';
1
2
  export type Driver = SyncDriver | AsyncDriver;
2
3
  export type Statement = SyncStatement | AsyncStatement;
3
4
  export interface BatchQuery {
@@ -16,7 +17,7 @@ export interface SyncDriver extends DriverSpecs {
16
17
  close(): void;
17
18
  exec(query: string): void;
18
19
  prepare(query: string, options?: PrepareOptions): SyncStatement;
19
- transaction<T>(run: (inner: SyncDriver) => T, options: unknown): T;
20
+ transaction<T>(run: (inner: SyncDriver) => T, options: TransactionUniversalOptions): T;
20
21
  batch(queries: Array<BatchQuery>): Array<Array<unknown>>;
21
22
  }
22
23
  export interface SyncStatement {
@@ -30,7 +31,7 @@ export interface AsyncDriver extends DriverSpecs {
30
31
  close(): Promise<void>;
31
32
  exec(query: string): Promise<void>;
32
33
  prepare(query: string, options?: PrepareOptions): AsyncStatement;
33
- transaction<T>(run: (inner: AsyncDriver) => Promise<T>, options: unknown): Promise<T>;
34
+ transaction<T>(run: (inner: AsyncDriver) => Promise<T>, options: TransactionUniversalOptions): Promise<T>;
34
35
  batch(queries: Array<BatchQuery>): Promise<Array<Array<unknown>>>;
35
36
  }
36
37
  export interface AsyncStatement {
@@ -32,7 +32,7 @@ export declare abstract class Query<Result, Meta extends QueryMeta> extends Exec
32
32
  get(db?: HasResolver): Deliver<Meta, Result | null>;
33
33
  run(db?: HasResolver): Deliver<Meta, void>;
34
34
  prepare<Inputs extends Record<string, unknown>>(name?: string): PreparedQuery<Result, Inputs, Meta>;
35
- toSQL(): {
35
+ toSQL(db?: HasResolver): {
36
36
  sql: string;
37
37
  params: Array<unknown>;
38
38
  };
@@ -80,8 +80,8 @@ var Query = class extends Executable {
80
80
  prepare(name) {
81
81
  return getData(this).resolver.prepare(this, name);
82
82
  }
83
- toSQL() {
84
- const { resolver } = getData(this);
83
+ toSQL(db) {
84
+ const resolver = db ? getResolver(db) : getData(this).resolver;
85
85
  if (!resolver)
86
86
  throw new Error("Query has no resolver");
87
87
  return resolver.toSQL(this);
@@ -29,6 +29,7 @@ export declare class Sql<Value = unknown> implements HasSql<Value> {
29
29
  field(field: FieldData): Sql<Value>;
30
30
  add(sql: HasSql): Sql<Value>;
31
31
  value(value: unknown): Sql<Value>;
32
+ getValue(): Value | undefined;
32
33
  inline(value: unknown): Sql<Value>;
33
34
  jsonPath(path: Array<string | number>): Sql<Value>;
34
35
  placeholder(name: string): Sql<Value>;
@@ -48,7 +49,7 @@ export declare namespace sql {
48
49
  export function identifier<T>(identifier: string): Sql<T>;
49
50
  export function field<T>(field: FieldData): Sql<T>;
50
51
  export function chunk<Type extends keyof EmitMethods>(type: Type, inner: Parameters<EmitMethods[Type]>[0]): Sql;
51
- export function universal(runtimes: Partial<Record<Runtime | 'default', Sql>>): Sql;
52
+ export function universal<T>(runtimes: Partial<Record<Runtime | 'default', Sql<T>>>): Sql<T>;
52
53
  type QueryChunk = HasSql | undefined;
53
54
  export function query(ast: Record<string, boolean | QueryChunk | Array<QueryChunk>>): Sql;
54
55
  export function join<T>(items: Array<Sql | HasSql | undefined | false>, separator?: Sql): Sql<T>;
package/dist/core/Sql.js CHANGED
@@ -46,6 +46,13 @@ var Sql = class _Sql {
46
46
  value(value) {
47
47
  return this.chunk("emitValue", value);
48
48
  }
49
+ getValue() {
50
+ if (this.#chunks.length !== 1)
51
+ return;
52
+ const chunk = this.#chunks[0];
53
+ if (chunk?.type === "emitValue")
54
+ return chunk.inner;
55
+ }
49
56
  inline(value) {
50
57
  return this.chunk("emitInline", value);
51
58
  }
@@ -141,7 +148,7 @@ function sql(strings, ...inner) {
141
148
  }
142
149
  sql2.chunk = chunk;
143
150
  function universal(runtimes) {
144
- return chunk("emitUniversal", runtimes);
151
+ return empty().chunk("emitUniversal", runtimes);
145
152
  }
146
153
  sql2.universal = universal;
147
154
  function query(ast) {
@@ -1,5 +1,5 @@
1
1
  // src/core/expr/Conditions.ts
2
- import { getQuery } from "../Internal.js";
2
+ import { getQuery, getSql, hasSql } from "../Internal.js";
3
3
  import { sql } from "../Sql.js";
4
4
  import { input } from "./Input.js";
5
5
  function bool(impl) {
@@ -41,19 +41,21 @@ function not(condition) {
41
41
  return bool(sql`not ${input(condition)}`);
42
42
  }
43
43
  function inArray(left, right) {
44
- if (Array.isArray(right)) {
45
- if (right.length === 0)
44
+ const value = (hasSql(right) ? getSql(right).getValue() : void 0) ?? right;
45
+ if (Array.isArray(value)) {
46
+ if (value.length === 0)
46
47
  return sql`false`;
47
- return bool(sql`${input(left)} in (${sql.join(right.map(input), sql`, `)})`);
48
+ return bool(sql`${input(left)} in (${sql.join(value.map(input), sql`, `)})`);
48
49
  }
49
50
  return bool(sql`${input(left)} in ${input(right)}`);
50
51
  }
51
52
  function notInArray(left, right) {
52
- if (Array.isArray(right)) {
53
- if (right.length === 0)
53
+ const value = (hasSql(right) ? getSql(right).getValue() : void 0) ?? right;
54
+ if (Array.isArray(value)) {
55
+ if (value.length === 0)
54
56
  return sql`true`;
55
57
  return bool(
56
- sql`${input(left)} not in (${sql.join(right.map(input), sql`, `)})`
58
+ sql`${input(left)} not in (${sql.join(value.map(input), sql`, `)})`
57
59
  );
58
60
  }
59
61
  return bool(sql`${input(left)} not in ${input(right)}`);
@@ -2,6 +2,7 @@
2
2
  import { SyncDatabase } from "../core/Database.js";
3
3
  import { sqliteDialect } from "../sqlite.js";
4
4
  import { sqliteDiff } from "../sqlite/diff.js";
5
+ import { execTransaction } from "../sqlite/transactions.js";
5
6
  var PreparedStatement = class {
6
7
  constructor(stmt, isSelection) {
7
8
  this.stmt = stmt;
@@ -25,9 +26,10 @@ var PreparedStatement = class {
25
26
  free() {
26
27
  }
27
28
  };
28
- var BetterSqlite3Driver = class {
29
- constructor(client) {
29
+ var BetterSqlite3Driver = class _BetterSqlite3Driver {
30
+ constructor(client, depth = 0) {
30
31
  this.client = client;
32
+ this.depth = depth;
31
33
  }
32
34
  parsesJson = false;
33
35
  exec(query) {
@@ -48,11 +50,13 @@ var BetterSqlite3Driver = class {
48
50
  );
49
51
  }
50
52
  transaction(run, options) {
51
- let result;
52
- this.client.transaction(() => {
53
- result = run(this);
54
- })[options.behavior ?? "deferred"]();
55
- return result;
53
+ return execTransaction(
54
+ this,
55
+ this.depth,
56
+ (depth) => new _BetterSqlite3Driver(this.client, depth),
57
+ run,
58
+ options
59
+ );
56
60
  }
57
61
  };
58
62
  function connect(db) {
@@ -2,6 +2,7 @@
2
2
  import { SyncDatabase } from "../core/Database.js";
3
3
  import { sqliteDialect } from "../sqlite.js";
4
4
  import { sqliteDiff } from "../sqlite/diff.js";
5
+ import { execTransaction } from "../sqlite/transactions.js";
5
6
  var PreparedStatement = class {
6
7
  constructor(stmt) {
7
8
  this.stmt = stmt;
@@ -22,9 +23,10 @@ var PreparedStatement = class {
22
23
  this.stmt.finalize();
23
24
  }
24
25
  };
25
- var BunSqliteDriver = class {
26
- constructor(client) {
26
+ var BunSqliteDriver = class _BunSqliteDriver {
27
+ constructor(client, depth = 0) {
27
28
  this.client = client;
29
+ this.depth = depth;
28
30
  }
29
31
  parsesJson = false;
30
32
  exec(query) {
@@ -42,11 +44,13 @@ var BunSqliteDriver = class {
42
44
  }, {});
43
45
  }
44
46
  transaction(run, options) {
45
- let result;
46
- this.client.transaction(() => {
47
- result = run(this);
48
- })[options.behavior ?? "deferred"]();
49
- return result;
47
+ return execTransaction(
48
+ this,
49
+ this.depth,
50
+ (depth) => new _BunSqliteDriver(this.client, depth),
51
+ run,
52
+ options
53
+ );
50
54
  }
51
55
  };
52
56
  function connect(db) {
@@ -2,6 +2,7 @@
2
2
  import { SyncDatabase } from "../core/Database.js";
3
3
  import { sqliteDialect } from "../sqlite.js";
4
4
  import { sqliteDiff } from "../sqlite/diff.js";
5
+ import { execTransaction } from "../sqlite/transactions.js";
5
6
  var PreparedStatement = class {
6
7
  constructor(client, stmt) {
7
8
  this.client = client;
@@ -59,16 +60,13 @@ var SqlJsDriver = class _SqlJsDriver {
59
60
  );
60
61
  }
61
62
  transaction(run, options) {
62
- const behavior = options.behavior ?? "deferred";
63
- this.exec(this.depth > 0 ? `savepoint d${this.depth}` : `begin ${behavior}`);
64
- try {
65
- const result = run(new _SqlJsDriver(this.client, this.depth + 1));
66
- this.exec(this.depth > 0 ? `release d${this.depth}` : "commit");
67
- return result;
68
- } catch (error) {
69
- this.exec(this.depth > 0 ? `rollback to d${this.depth}` : "rollback");
70
- throw error;
71
- }
63
+ return execTransaction(
64
+ this,
65
+ this.depth,
66
+ (depth) => new _SqlJsDriver(this.client, depth),
67
+ run,
68
+ options
69
+ );
72
70
  }
73
71
  };
74
72
  function connect(db) {
@@ -1,14 +1,10 @@
1
- import type { HasQuery, HasSql, HasTable } from '../core/Internal.js';
1
+ import type { HasSql, HasTable } from '../core/Internal.js';
2
2
  import { type Sql } from '../core/Sql.js';
3
3
  import { type Input } from '../core/expr/Input.js';
4
4
  /**: The count(X) function returns a count of the number of times that X is not NULL in a group. The count(*) function (with no arguments) returns the total number of rows in the group. */
5
5
  export declare const count: (x?: HasSql) => Sql<number>;
6
6
  /** The iif(X,Y,Z) function returns the value Y if X is true, and Z otherwise. The iif(X,Y,Z) function is logically equivalent to and generates the same bytecode as the CASE HasSql "CASE WHEN X THEN Y ELSE Z END".*/
7
7
  export declare const iif: <T>(x: Input<boolean>, y: Input<T>, z: Input<T>) => Sql<T>;
8
- /**
9
- * The EXISTS operator always evaluates to one of the integer values 0 and 1. If executing the SELECT statement specified as the right-hand operand of the EXISTS operator would return one or more rows, then the EXISTS operator evaluates to 1. If executing the SELECT would return no rows at all, then the EXISTS operator evaluates to 0.
10
- */
11
- export declare const exists: (cursor: HasQuery) => Sql<boolean>;
12
8
  /** Use the match operator for the FTS5 module */
13
9
  export declare const match: (table: HasTable, searchTerm: Input<string>) => Sql<boolean>;
14
10
  /** Returns a real value reflecting the accuracy of the current match */
@@ -4,7 +4,6 @@ import { Functions } from "../core/expr/Functions.js";
4
4
  import { input } from "../core/expr/Input.js";
5
5
  var count = Functions.count;
6
6
  var iif = Functions.iif;
7
- var exists = Functions.exists;
8
7
  var match = Functions.match;
9
8
  var bm25 = Functions.bm25;
10
9
  var highlight = Functions.highlight;
@@ -101,7 +100,6 @@ export {
101
100
  date,
102
101
  datetime,
103
102
  degrees,
104
- exists,
105
103
  exp,
106
104
  floor,
107
105
  group_concat,
@@ -0,0 +1,3 @@
1
+ import type { TransactionOptions } from '../core/Database.js';
2
+ import type { SyncDriver } from '../core/Driver.js';
3
+ export declare function execTransaction<T>(driver: SyncDriver, depth: number, wrap: (depth: number) => SyncDriver, run: (inner: SyncDriver) => T, options: TransactionOptions['sqlite']): T;
@@ -0,0 +1,35 @@
1
+ // src/sqlite/transactions.ts
2
+ var locks = /* @__PURE__ */ new WeakMap();
3
+ function execTransaction(driver, depth, wrap, run, options) {
4
+ const needsLock = options.async;
5
+ const behavior = options.behavior ?? "deferred";
6
+ if (!needsLock)
7
+ return transact();
8
+ let trigger;
9
+ const lock = new Promise((resolve) => trigger = resolve);
10
+ const current = Promise.resolve(locks.get(driver));
11
+ locks.set(driver, lock);
12
+ return current.then(transact).finally(trigger);
13
+ function transact() {
14
+ try {
15
+ driver.exec(depth > 0 ? `savepoint d${depth}` : `begin ${behavior}`);
16
+ const result = run(wrap(depth + 1));
17
+ if (result instanceof Promise)
18
+ return result.then(release, rollback);
19
+ return release(result);
20
+ } catch (error) {
21
+ return rollback(error);
22
+ }
23
+ }
24
+ function release(result) {
25
+ driver.exec(depth > 0 ? `release d${depth}` : "commit");
26
+ return result;
27
+ }
28
+ function rollback(error) {
29
+ driver.exec(depth > 0 ? `rollback to d${depth}` : "rollback");
30
+ throw error;
31
+ }
32
+ }
33
+ export {
34
+ execTransaction
35
+ };
@@ -1,4 +1,4 @@
1
1
  import { type Sql } from '../core/Sql.js';
2
- import type { Input } from '../core/expr/Input.js';
2
+ import { type Input } from '../core/expr/Input.js';
3
3
  export declare function lastInsertId(): Sql<number>;
4
4
  export declare function concat(...slices: Array<Input<string | null>>): Sql<string>;
@@ -1,16 +1,20 @@
1
1
  // src/universal/functions.ts
2
2
  import { sql } from "../core/Sql.js";
3
- import { callFunction } from "../core/expr/Functions.js";
3
+ import { Functions } from "../core/expr/Functions.js";
4
+ import { input } from "../core/expr/Input.js";
4
5
  var insertId = sql.universal({
5
- sqlite: sql`last_insert_rowid()`,
6
- postgres: sql`lastval()`,
7
- mysql: sql`last_insert_id()`
6
+ sqlite: Functions.last_insert_rowid(),
7
+ postgres: Functions.lastval(),
8
+ mysql: Functions.last_insert_id()
8
9
  }).mapWith(Number);
9
10
  function lastInsertId() {
10
11
  return insertId;
11
12
  }
12
13
  function concat(...slices) {
13
- return callFunction(sql`concat`, slices);
14
+ return sql.universal({
15
+ mysql: Functions.concat(...slices),
16
+ default: sql.join(slices.map(input), sql` || `)
17
+ });
14
18
  }
15
19
  export {
16
20
  concat,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rado",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {