rado 0.2.36 → 0.2.39

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.
@@ -7,6 +7,6 @@ export interface SchemaInstructions {
7
7
  }
8
8
  export declare namespace Schema {
9
9
  function create(schema: TableData): QueryData.Batch;
10
- function upgrade(formatter: Formatter, local: SchemaInstructions, schema: TableData): Array<QueryData>;
10
+ function upgrade(formatter: Formatter, local: SchemaInstructions, schema: TableData, verbose?: boolean): Array<QueryData>;
11
11
  }
12
12
  export declare function schema(...tables: Array<Table<any>>): void;
@@ -53,7 +53,7 @@ var Schema;
53
53
  queries.push(new QueryData.CreateIndex({ table, index }));
54
54
  return queries;
55
55
  }
56
- function upgrade(formatter, local, schema2) {
56
+ function upgrade(formatter, local, schema2, verbose = true) {
57
57
  const columnNames = /* @__PURE__ */ new Set([
58
58
  ...Object.keys(local.columns),
59
59
  ...Object.keys(schema2.columns)
@@ -67,16 +67,22 @@ var Schema;
67
67
  res.push(
68
68
  new QueryData.AlterTable({ table: schema2, addColumn: schemaCol })
69
69
  );
70
+ if (verbose)
71
+ console.log(`Adding column ${columnName}`);
70
72
  } else if (!schemaCol) {
71
73
  res.push(
72
74
  new QueryData.AlterTable({ table: schema2, dropColumn: columnName })
73
75
  );
76
+ if (verbose)
77
+ console.log(`Removing column ${columnName}`);
74
78
  } else {
75
79
  const { sql: instruction } = formatter.formatColumn(
76
80
  { stmt: new Statement(formatter) },
77
81
  { ...schemaCol, references: void 0 }
78
82
  );
79
83
  if (removeLeadingWhitespace(localInstruction) !== removeLeadingWhitespace(instruction)) {
84
+ if (verbose)
85
+ console.log(`Recreating because ${columnName} differs`);
80
86
  recreate = true;
81
87
  }
82
88
  }
@@ -97,13 +103,19 @@ var Schema;
97
103
  const schemaIndex = meta.indexes[indexName];
98
104
  if (!localInstruction) {
99
105
  res.push(new QueryData.CreateIndex({ table: schema2, index: schemaIndex }));
106
+ if (verbose)
107
+ console.log(`Adding index ${indexName}`);
100
108
  } else if (!schemaIndex) {
101
109
  res.unshift(new QueryData.DropIndex({ table: schema2, name: indexName }));
110
+ if (verbose)
111
+ console.log(`Removing index ${indexName}`);
102
112
  } else {
103
113
  const { sql: instruction } = formatter.compile(
104
114
  new QueryData.CreateIndex({ table: schema2, index: schemaIndex })
105
115
  );
106
116
  if (removeLeadingWhitespace(localInstruction) !== removeLeadingWhitespace(instruction)) {
117
+ if (verbose)
118
+ console.log(`Recreating index ${indexName}`);
107
119
  res.unshift(new QueryData.DropIndex({ table: schema2, name: indexName }));
108
120
  res.push(
109
121
  new QueryData.CreateIndex({ table: schema2, index: schemaIndex })
@@ -1,15 +1,15 @@
1
1
  import type { Database } from 'better-sqlite3';
2
2
  import { SchemaInstructions } from '../define/Schema.js';
3
- import { Driver } from '../lib/Driver.js';
3
+ import { Driver, DriverOptions } from '../lib/Driver.js';
4
4
  import { Statement } from '../lib/Statement.js';
5
5
  import { SqliteSchema } from '../sqlite/SqliteSchema.js';
6
6
  export declare class BetterSqlite3Driver extends Driver.Sync {
7
7
  db: Database;
8
8
  tableData: (tableName: string) => Array<SqliteSchema.Column>;
9
9
  indexData: (tableName: string) => Array<SqliteSchema.Index>;
10
- constructor(db: Database);
10
+ constructor(db: Database, options?: DriverOptions);
11
11
  prepareStatement(stmt: Statement): Driver.Sync.PreparedStatement;
12
12
  schemaInstructions(tableName: string): SchemaInstructions | undefined;
13
13
  export(): Uint8Array;
14
14
  }
15
- export declare function connect(db: Database): BetterSqlite3Driver;
15
+ export declare function connect(db: Database, options?: DriverOptions): BetterSqlite3Driver;
@@ -1,5 +1,4 @@
1
1
  import { Driver } from "../lib/Driver.js";
2
- import { SqlError } from "../lib/SqlError.js";
3
2
  import { SqliteFormatter } from "../sqlite/SqliteFormatter.js";
4
3
  import { SqliteSchema } from "../sqlite/SqliteSchema.js";
5
4
  class PreparedStatement {
@@ -23,8 +22,8 @@ class PreparedStatement {
23
22
  }
24
23
  }
25
24
  class BetterSqlite3Driver extends Driver.Sync {
26
- constructor(db) {
27
- super(new SqliteFormatter());
25
+ constructor(db, options) {
26
+ super(new SqliteFormatter(), options);
28
27
  this.db = db;
29
28
  this.tableData = this.prepare(SqliteSchema.tableData);
30
29
  this.indexData = this.prepare(SqliteSchema.indexData);
@@ -32,11 +31,7 @@ class BetterSqlite3Driver extends Driver.Sync {
32
31
  tableData;
33
32
  indexData;
34
33
  prepareStatement(stmt) {
35
- try {
36
- return new PreparedStatement(this.db.prepare(stmt.sql));
37
- } catch (e) {
38
- throw new SqlError(e, stmt.sql);
39
- }
34
+ return new PreparedStatement(this.db.prepare(stmt.sql));
40
35
  }
41
36
  schemaInstructions(tableName) {
42
37
  const columnData = this.tableData(tableName);
@@ -47,8 +42,8 @@ class BetterSqlite3Driver extends Driver.Sync {
47
42
  return this.db.serialize();
48
43
  }
49
44
  }
50
- function connect(db) {
51
- return new BetterSqlite3Driver(db);
45
+ function connect(db, options) {
46
+ return new BetterSqlite3Driver(db, options);
52
47
  }
53
48
  export {
54
49
  BetterSqlite3Driver,
@@ -1,7 +1,7 @@
1
1
  /// <reference types="bun-types" />
2
2
  import type { Database } from 'bun:sqlite';
3
3
  import { SchemaInstructions } from '../define/Schema.js';
4
- import { Driver } from '../lib/Driver.js';
4
+ import { Driver, DriverOptions } from '../lib/Driver.js';
5
5
  import { Statement } from '../lib/Statement.js';
6
6
  import { SqliteSchema } from '../sqlite/SqliteSchema.js';
7
7
  export declare class BunSqliteDriver extends Driver.Sync {
@@ -11,8 +11,8 @@ export declare class BunSqliteDriver extends Driver.Sync {
11
11
  lastChanges: () => {
12
12
  rowsAffected: number;
13
13
  };
14
- constructor(db: Database);
14
+ constructor(db: Database, options?: DriverOptions);
15
15
  prepareStatement(stmt: Statement): Driver.Sync.PreparedStatement;
16
16
  schemaInstructions(tableName: string): SchemaInstructions | undefined;
17
17
  }
18
- export declare function connect(db: Database): BunSqliteDriver;
18
+ export declare function connect(db: Database, options?: DriverOptions): BunSqliteDriver;
@@ -1,7 +1,6 @@
1
1
  import { QueryData } from "../define/Query.js";
2
2
  import { SelectFirst } from "../define/query/Select.js";
3
3
  import { Driver } from "../lib/Driver.js";
4
- import { SqlError } from "../lib/SqlError.js";
5
4
  import { SqliteFormatter } from "../sqlite/SqliteFormatter.js";
6
5
  import { SqliteSchema } from "../sqlite/SqliteSchema.js";
7
6
  class PreparedStatement {
@@ -28,8 +27,8 @@ class PreparedStatement {
28
27
  }
29
28
  }
30
29
  class BunSqliteDriver extends Driver.Sync {
31
- constructor(db) {
32
- super(new SqliteFormatter());
30
+ constructor(db, options) {
31
+ super(new SqliteFormatter(), options);
33
32
  this.db = db;
34
33
  this.tableData = this.prepare(SqliteSchema.tableData);
35
34
  this.indexData = this.prepare(SqliteSchema.indexData);
@@ -47,11 +46,7 @@ class BunSqliteDriver extends Driver.Sync {
47
46
  indexData;
48
47
  lastChanges;
49
48
  prepareStatement(stmt) {
50
- try {
51
- return new PreparedStatement(this.lastChanges, this.db.prepare(stmt.sql));
52
- } catch (e) {
53
- throw new SqlError(e, stmt.sql);
54
- }
49
+ return new PreparedStatement(this.lastChanges, this.db.prepare(stmt.sql));
55
50
  }
56
51
  schemaInstructions(tableName) {
57
52
  const columnData = this.tableData(tableName);
@@ -59,8 +54,8 @@ class BunSqliteDriver extends Driver.Sync {
59
54
  return SqliteSchema.createInstructions(columnData, indexData);
60
55
  }
61
56
  }
62
- function connect(db) {
63
- return new BunSqliteDriver(db);
57
+ function connect(db, options) {
58
+ return new BunSqliteDriver(db, options);
64
59
  }
65
60
  export {
66
61
  BunSqliteDriver,
@@ -1,15 +1,15 @@
1
1
  import type { Database } from 'sql.js';
2
2
  import { SchemaInstructions } from '../define/Schema.js';
3
- import { Driver } from '../lib/Driver.js';
3
+ import { Driver, DriverOptions } from '../lib/Driver.js';
4
4
  import { Statement } from '../lib/Statement.js';
5
5
  import { SqliteSchema } from '../sqlite/SqliteSchema.js';
6
6
  export declare class SqlJsDriver extends Driver.Sync {
7
7
  db: Database;
8
8
  tableData?: (tableName: string) => Array<SqliteSchema.Column>;
9
9
  indexData?: (tableName: string) => Array<SqliteSchema.Index>;
10
- constructor(db: Database);
10
+ constructor(db: Database, options?: DriverOptions);
11
11
  prepareStatement(stmt: Statement, discardAfter: boolean): Driver.Sync.PreparedStatement;
12
12
  schemaInstructions(tableName: string): SchemaInstructions | undefined;
13
13
  export(): Uint8Array;
14
14
  }
15
- export declare function connect(db: Database): SqlJsDriver;
15
+ export declare function connect(db: Database, options?: DriverOptions): SqlJsDriver;
@@ -1,5 +1,4 @@
1
1
  import { Driver } from "../lib/Driver.js";
2
- import { SqlError } from "../lib/SqlError.js";
3
2
  import { SqliteFormatter } from "../sqlite/SqliteFormatter.js";
4
3
  import { SqliteSchema } from "../sqlite/SqliteSchema.js";
5
4
  class PreparedStatement {
@@ -34,22 +33,18 @@ class PreparedStatement {
34
33
  }
35
34
  }
36
35
  class SqlJsDriver extends Driver.Sync {
37
- constructor(db) {
38
- super(new SqliteFormatter());
36
+ constructor(db, options) {
37
+ super(new SqliteFormatter(), options);
39
38
  this.db = db;
40
39
  }
41
40
  tableData;
42
41
  indexData;
43
42
  prepareStatement(stmt, discardAfter) {
44
- try {
45
- return new PreparedStatement(
46
- this.db,
47
- this.db.prepare(stmt.sql),
48
- discardAfter
49
- );
50
- } catch (e) {
51
- throw new SqlError(e, stmt.sql);
52
- }
43
+ return new PreparedStatement(
44
+ this.db,
45
+ this.db.prepare(stmt.sql),
46
+ discardAfter
47
+ );
53
48
  }
54
49
  schemaInstructions(tableName) {
55
50
  this.tableData = this.tableData || (this.tableData = this.prepare(SqliteSchema.tableData));
@@ -62,8 +57,8 @@ class SqlJsDriver extends Driver.Sync {
62
57
  return this.db.export();
63
58
  }
64
59
  }
65
- function connect(db) {
66
- return new SqlJsDriver(db);
60
+ function connect(db, options) {
61
+ return new SqlJsDriver(db, options);
67
62
  }
68
63
  export {
69
64
  SqlJsDriver,
@@ -1,7 +1,7 @@
1
1
  import type { Database } from 'sqlite3';
2
2
  import { QueryData } from '../define/Query.js';
3
3
  import { SchemaInstructions } from '../define/Schema.js';
4
- import { Driver } from '../lib/Driver.js';
4
+ import { Driver, DriverOptions } from '../lib/Driver.js';
5
5
  import { Statement } from '../lib/Statement.js';
6
6
  import { SqliteSchema } from '../sqlite/SqliteSchema.js';
7
7
  export declare class Sqlite3Driver extends Driver.Async {
@@ -9,10 +9,10 @@ export declare class Sqlite3Driver extends Driver.Async {
9
9
  lock: Promise<void> | undefined;
10
10
  tableData: (tableName: string) => Promise<Array<SqliteSchema.Column>>;
11
11
  indexData: (tableName: string) => Promise<Array<SqliteSchema.Index>>;
12
- constructor(db: Database);
12
+ constructor(db: Database, options?: DriverOptions);
13
13
  executeQuery(query: QueryData, stmt?: Driver.Async.PreparedStatement, params?: any[] | undefined): Promise<unknown>;
14
14
  prepareStatement(stmt: Statement): Driver.Async.PreparedStatement;
15
15
  schemaInstructions(tableName: string): Promise<SchemaInstructions | undefined>;
16
16
  isolate(): [connection: Driver.Async, release: () => Promise<void>];
17
17
  }
18
- export declare function connect(db: Database): Sqlite3Driver;
18
+ export declare function connect(db: Database, options?: DriverOptions): Sqlite3Driver;
@@ -1,5 +1,4 @@
1
1
  import { Driver } from "../lib/Driver.js";
2
- import { SqlError } from "../lib/SqlError.js";
3
2
  import { SqliteFormatter } from "../sqlite/SqliteFormatter.js";
4
3
  import { SqliteSchema } from "../sqlite/SqliteSchema.js";
5
4
  class PreparedStatement {
@@ -7,7 +6,9 @@ class PreparedStatement {
7
6
  this.stmt = stmt;
8
7
  }
9
8
  async *iterate(params) {
10
- let rows = [], done = false, error;
9
+ let rows = [];
10
+ let done = false;
11
+ let error;
11
12
  let resolve;
12
13
  let promise = new Promise((r) => resolve = r);
13
14
  this.stmt.each(
@@ -79,8 +80,8 @@ class PreparedStatement {
79
80
  }
80
81
  }
81
82
  class Sqlite3Driver extends Driver.Async {
82
- constructor(db) {
83
- super(new SqliteFormatter());
83
+ constructor(db, options) {
84
+ super(new SqliteFormatter(), options);
84
85
  this.db = db;
85
86
  this.tableData = this.prepare(SqliteSchema.tableData);
86
87
  this.indexData = this.prepare(SqliteSchema.indexData);
@@ -93,11 +94,7 @@ class Sqlite3Driver extends Driver.Async {
93
94
  return super.executeQuery(query, stmt, params);
94
95
  }
95
96
  prepareStatement(stmt) {
96
- try {
97
- return new PreparedStatement(this.db.prepare(stmt.sql));
98
- } catch (e) {
99
- throw new SqlError(e, stmt.sql);
100
- }
97
+ return new PreparedStatement(this.db.prepare(stmt.sql));
101
98
  }
102
99
  async schemaInstructions(tableName) {
103
100
  const columnData = await this.tableData(tableName);
@@ -113,8 +110,8 @@ class Sqlite3Driver extends Driver.Async {
113
110
  return [connection, release];
114
111
  }
115
112
  }
116
- function connect(db) {
117
- return new Sqlite3Driver(db);
113
+ function connect(db, options) {
114
+ return new Sqlite3Driver(db, options);
118
115
  }
119
116
  export {
120
117
  Sqlite3Driver,
package/dist/index.d.ts CHANGED
@@ -22,7 +22,7 @@ export * from './define/query/Select.js';
22
22
  export * from './define/query/TableSelect.js';
23
23
  export * from './define/query/Union.js';
24
24
  export * from './define/query/Update.js';
25
- export type { Driver } from './lib/Driver.js';
25
+ export type { Driver, DriverOptions } from './lib/Driver.js';
26
26
  export type { FormatContext, Formatter } from './lib/Formatter.js';
27
27
  export type { Sanitizer } from './lib/Sanitizer.js';
28
28
  export * from './lib/SqlError.js';
@@ -6,9 +6,13 @@ import { Select } from '../define/query/Select.js';
6
6
  import { Callable } from '../util/Callable.js';
7
7
  import { Formatter } from './Formatter.js';
8
8
  import { Statement } from './Statement.js';
9
+ export interface DriverOptions {
10
+ logQuery?: (stmt: Statement, durationMs: number) => void;
11
+ }
9
12
  declare abstract class DriverBase extends Callable {
10
13
  formatter: Formatter;
11
- constructor(formatter: Formatter);
14
+ options: DriverOptions;
15
+ constructor(formatter: Formatter, options: DriverOptions);
12
16
  compile<T extends Array<Expr<any>>, R>(create: (...params: T) => Query<R>): [QueryData, Statement];
13
17
  executeTemplate(expectedReturn: QueryData.RawReturn, strings: TemplateStringsArray, ...params: Array<any>): any;
14
18
  abstract executeQuery(query: QueryData): any;
@@ -25,9 +29,11 @@ interface SyncDriver {
25
29
  }
26
30
  declare abstract class SyncDriver extends DriverBase {
27
31
  transactionId: number;
28
- constructor(formatter: Formatter);
32
+ constructor(formatter: Formatter, options?: DriverOptions);
29
33
  abstract prepareStatement(stmt: Statement, discardAfter: boolean): SyncPreparedStatement;
30
34
  abstract schemaInstructions(tableName: string): SchemaInstructions | undefined;
35
+ createStatement(stmt: Statement, discardAfter: boolean): SyncPreparedStatement;
36
+ handle(stmt: Statement, prepared: SyncPreparedStatement): SyncPreparedStatement;
31
37
  prepare<T extends Array<Expr<any>>, R>(create: (...params: T) => Query<R>): (...params: ParamTypes<T>) => R;
32
38
  migrateSchema(...tables: Array<Table<any>>): unknown;
33
39
  executeQuery(query: QueryData, stmt?: SyncPreparedStatement, params?: Array<any>): unknown;
@@ -51,10 +57,12 @@ interface AsyncDriver {
51
57
  }
52
58
  declare abstract class AsyncDriver extends DriverBase {
53
59
  transactionId: number;
54
- constructor(formatter: Formatter);
60
+ constructor(formatter: Formatter, options?: DriverOptions);
55
61
  abstract isolate(): [connection: AsyncDriver, release: () => Promise<void>];
56
62
  abstract prepareStatement(stmt: Statement, discardAfter: boolean): AsyncPreparedStatement;
57
63
  abstract schemaInstructions(tableName: string): Promise<SchemaInstructions | undefined>;
64
+ createStatement(stmt: Statement, discardAfter: boolean): AsyncPreparedStatement;
65
+ handle(stmt: Statement, prepared: AsyncPreparedStatement): AsyncPreparedStatement;
58
66
  prepare<T extends Array<Expr<any>>, R>(create: (...params: T) => Query<R>): (...params: ParamTypes<T>) => Promise<R>;
59
67
  migrateSchema(...tables: Array<Table<any>>): Promise<unknown>;
60
68
  executeQuery(query: QueryData, stmt?: AsyncPreparedStatement, params?: Array<any>): Promise<unknown>;
@@ -4,11 +4,12 @@ import { Query, QueryData, QueryType } from "../define/Query.js";
4
4
  import { Schema } from "../define/Schema.js";
5
5
  import { Table } from "../define/Table.js";
6
6
  import { Callable } from "../util/Callable.js";
7
+ import { SqlError } from "./SqlError.js";
7
8
  function isTemplateStringsArray(input) {
8
9
  return Boolean(Array.isArray(input) && input.raw);
9
10
  }
10
11
  class DriverBase extends Callable {
11
- constructor(formatter) {
12
+ constructor(formatter, options) {
12
13
  super((...args) => {
13
14
  const [input, ...rest] = args;
14
15
  if (Query.isQuery(input) && rest.length === 0)
@@ -22,6 +23,7 @@ class DriverBase extends Callable {
22
23
  );
23
24
  });
24
25
  this.formatter = formatter;
26
+ this.options = options;
25
27
  }
26
28
  compile(create) {
27
29
  const { length } = create;
@@ -41,13 +43,41 @@ class DriverBase extends Callable {
41
43
  }
42
44
  class SyncDriver extends DriverBase {
43
45
  transactionId;
44
- constructor(formatter) {
45
- super(formatter);
46
+ constructor(formatter, options = {}) {
47
+ super(formatter, options);
46
48
  this.transactionId = 0;
47
49
  }
50
+ createStatement(stmt, discardAfter) {
51
+ try {
52
+ return this.handle(stmt, this.prepareStatement(stmt, discardAfter));
53
+ } catch (e) {
54
+ throw new SqlError(e, stmt.sql);
55
+ }
56
+ }
57
+ handle(stmt, prepared) {
58
+ const { logQuery } = this.options;
59
+ const wrap = (fn) => (...args) => {
60
+ const startTime = performance.now();
61
+ try {
62
+ const res = fn(...args);
63
+ if (logQuery)
64
+ logQuery(stmt, performance.now() - startTime);
65
+ return res;
66
+ } catch (e) {
67
+ throw new SqlError(e, stmt.sql);
68
+ }
69
+ };
70
+ return {
71
+ run: wrap(prepared.run.bind(prepared)),
72
+ iterate: wrap(prepared.iterate.bind(prepared)),
73
+ all: wrap(prepared.all.bind(prepared)),
74
+ get: wrap(prepared.get.bind(prepared)),
75
+ execute: wrap(prepared.execute.bind(prepared))
76
+ };
77
+ }
48
78
  prepare(create) {
49
79
  const [query, compiled] = this.compile(create);
50
- const prepared = this.prepareStatement(compiled, false);
80
+ const prepared = this.createStatement(compiled, false);
51
81
  return (...params) => {
52
82
  const namedParams = Object.fromEntries(
53
83
  params.map((value, i) => [`p${i}`, value])
@@ -90,7 +120,7 @@ class SyncDriver extends DriverBase {
90
120
  });
91
121
  default:
92
122
  const compiled = stmt ? void 0 : this.formatter.compile(query);
93
- stmt = stmt || this.prepareStatement(compiled, true);
123
+ stmt = stmt || this.createStatement(compiled, true);
94
124
  params = params || compiled.params();
95
125
  if ("selection" in query || query.type === QueryType.Union) {
96
126
  const res = stmt.all(params).map((row) => JSON.parse(row.result).result);
@@ -117,7 +147,7 @@ class SyncDriver extends DriverBase {
117
147
  }
118
148
  }
119
149
  *iterate(cursor) {
120
- const stmt = this.prepareStatement(
150
+ const stmt = this.createStatement(
121
151
  this.formatter.compile(cursor[Query.Data]),
122
152
  true
123
153
  );
@@ -155,13 +185,48 @@ class SyncDriver extends DriverBase {
155
185
  }
156
186
  class AsyncDriver extends DriverBase {
157
187
  transactionId;
158
- constructor(formatter) {
159
- super(formatter);
188
+ constructor(formatter, options = {}) {
189
+ super(formatter, options);
160
190
  this.transactionId = 0;
161
191
  }
192
+ createStatement(stmt, discardAfter) {
193
+ try {
194
+ return this.handle(stmt, this.prepareStatement(stmt, discardAfter));
195
+ } catch (e) {
196
+ throw new SqlError(e, stmt.sql);
197
+ }
198
+ }
199
+ handle(stmt, prepared) {
200
+ const { logQuery } = this.options;
201
+ const wrap = (fn) => async (...args) => {
202
+ const startTime = performance.now();
203
+ try {
204
+ const res = await fn(...args);
205
+ if (logQuery)
206
+ logQuery(stmt, (performance.now() - startTime) / 1e3);
207
+ return res;
208
+ } catch (e) {
209
+ throw new SqlError(e, stmt.sql);
210
+ }
211
+ };
212
+ return {
213
+ run: wrap(prepared.run.bind(prepared)),
214
+ async *iterate(params) {
215
+ const iterator = prepared.iterate(params);
216
+ try {
217
+ yield* iterator;
218
+ } catch (e) {
219
+ throw new SqlError(e, stmt.sql);
220
+ }
221
+ },
222
+ all: wrap(prepared.all.bind(prepared)),
223
+ get: wrap(prepared.get.bind(prepared)),
224
+ execute: wrap(prepared.execute.bind(prepared))
225
+ };
226
+ }
162
227
  prepare(create) {
163
228
  const [query, compiled] = this.compile(create);
164
- const prepared = this.prepareStatement(compiled, false);
229
+ const prepared = this.createStatement(compiled, false);
165
230
  return (...params) => {
166
231
  const namedParams = Object.fromEntries(
167
232
  params.map((value, i) => [`p${i}`, value])
@@ -204,7 +269,7 @@ class AsyncDriver extends DriverBase {
204
269
  });
205
270
  default:
206
271
  const compiled = stmt ? void 0 : this.formatter.compile(query);
207
- stmt = stmt || this.prepareStatement(compiled, true);
272
+ stmt = stmt || this.createStatement(compiled, true);
208
273
  params = params || compiled.params();
209
274
  if ("selection" in query || query.type === QueryType.Union) {
210
275
  const res = (await stmt.all(params)).map(
@@ -233,7 +298,7 @@ class AsyncDriver extends DriverBase {
233
298
  }
234
299
  }
235
300
  async *iterate(cursor) {
236
- const stmt = this.prepareStatement(
301
+ const stmt = this.createStatement(
237
302
  this.formatter.compile(cursor[Query.Data]),
238
303
  true
239
304
  );
@@ -292,7 +357,7 @@ class SyncPreparedStatementWrapper {
292
357
  }
293
358
  class SyncWrapper extends AsyncDriver {
294
359
  constructor(sync) {
295
- super(sync.formatter);
360
+ super(sync.formatter, sync.options);
296
361
  this.sync = sync;
297
362
  }
298
363
  lock;
@@ -424,7 +424,7 @@ class Formatter {
424
424
  const { stmt } = ctx;
425
425
  if (!expr)
426
426
  return stmt;
427
- stmt.newline().raw("WHERE").space();
427
+ stmt.addLine("WHERE").space();
428
428
  this.formatExprValue(ctx, expr);
429
429
  return stmt;
430
430
  }
@@ -432,7 +432,7 @@ class Formatter {
432
432
  const { stmt } = ctx;
433
433
  if (!expr)
434
434
  return stmt;
435
- stmt.newline().raw("HAVING");
435
+ stmt.addLine("HAVING");
436
436
  this.formatExprValue(ctx, expr);
437
437
  return stmt;
438
438
  }
@@ -440,7 +440,7 @@ class Formatter {
440
440
  const { stmt } = ctx;
441
441
  if (!groupBy)
442
442
  return stmt;
443
- stmt.newline().raw("GROUP BY");
443
+ stmt.addLine("GROUP BY");
444
444
  for (const expr of stmt.separate(groupBy))
445
445
  this.formatExprValue(ctx, expr);
446
446
  return stmt;
@@ -449,7 +449,7 @@ class Formatter {
449
449
  const { stmt } = ctx;
450
450
  if (!orderBy)
451
451
  return stmt;
452
- stmt.newline().raw("ORDER BY").space();
452
+ stmt.addLine("ORDER BY").space();
453
453
  for (const { expr, order } of stmt.separate(orderBy)) {
454
454
  this.formatExprValue(ctx, expr);
455
455
  stmt.add(order === OrderDirection.Asc ? "ASC" : "DESC");
@@ -460,7 +460,7 @@ class Formatter {
460
460
  const { stmt, forceInline } = ctx;
461
461
  if (!limit && !offset && !singleResult)
462
462
  return stmt;
463
- stmt.newline().raw("LIMIT").space();
463
+ stmt.addLine("LIMIT").space();
464
464
  this.formatValue(ctx, singleResult ? 1 : limit);
465
465
  if (offset && offset > 0) {
466
466
  stmt.add("OFFSET").space();
@@ -678,7 +678,7 @@ class Formatter {
678
678
  const { stmt } = ctx;
679
679
  if (!ctx.formatAsJson) {
680
680
  stmt.openParenthesis();
681
- this.format(ctx, expr.query);
681
+ this.format({ ...ctx, formatAsIn: false }, expr.query);
682
682
  return stmt.closeParenthesis();
683
683
  }
684
684
  if (expr.query.singleResult) {
@@ -1,3 +1,3 @@
1
1
  export declare class SqlError extends Error {
2
- constructor(cause: Error, sql: string);
2
+ constructor(cause: unknown, sql: string);
3
3
  }
@@ -1,8 +1,15 @@
1
1
  import { ParamData } from '../define/Param.js';
2
2
  import { Sanitizer } from './Sanitizer.js';
3
3
  export interface StatementOptions {
4
+ /**
5
+ * Do not add any newlines while composing the statement
6
+ */
4
7
  skipNewlines?: boolean;
5
8
  }
9
+ /**
10
+ * A statement is a query string builder with parameter placeholders, and a list
11
+ * of parameter values.
12
+ */
6
13
  export declare class Statement {
7
14
  sanitizer: Sanitizer;
8
15
  options: StatementOptions;
@@ -10,24 +17,92 @@ export declare class Statement {
10
17
  paramData: Array<ParamData>;
11
18
  currentIndent: string;
12
19
  constructor(sanitizer: Sanitizer, options?: StatementOptions);
13
- space(): this;
20
+ /**
21
+ * UNSAFE
22
+ * Adds a string without applying escaping
23
+ */
24
+ raw(query: string): this;
25
+ /**
26
+ * UNSAFE
27
+ * Adds a space if the statement is not empty, and then adds the given string
28
+ * without applying escaping
29
+ */
14
30
  add(addition: undefined | string): this;
31
+ /**
32
+ * UNSAFE
33
+ * Adds a newline if the statement is not empty, and then adds the given
34
+ * string without applying escaping
35
+ */
15
36
  addLine(addition: undefined | string): this;
37
+ /**
38
+ * Adds a space if the statement is not empty
39
+ */
40
+ space(): this;
41
+ /**
42
+ * Increases the indentation level
43
+ */
16
44
  indent(): this;
45
+ /**
46
+ * Decreases the indentation level
47
+ */
17
48
  dedent(): this;
49
+ /**
50
+ * Adds a newline
51
+ */
18
52
  newline(): this;
53
+ /**
54
+ * Adds an identifier, applies escaping
55
+ */
19
56
  identifier(name: string): this;
57
+ /**
58
+ * Adds a space if the statement is not empty, and then adds an identifier
59
+ */
20
60
  addIdentifier(name: string): this;
61
+ /**
62
+ * Adds a value, applies escaping
63
+ */
21
64
  value(value: any): this;
65
+ /**
66
+ * Adds a space if the statement is not empty, and then adds a value
67
+ */
22
68
  addValue(value: any): this;
69
+ /**
70
+ * Adds a parameter
71
+ */
23
72
  param(data: ParamData): this;
73
+ /**
74
+ * Adds a space if the statement is not empty, and then adds a parameter
75
+ */
24
76
  addParam(data: ParamData): this;
25
- raw(query: string): this;
77
+ /**
78
+ * Opens a parenthesis and indents the next line
79
+ */
26
80
  openParenthesis(): this;
81
+ /**
82
+ * Closes a parenthesis and dedents the next line
83
+ */
27
84
  closeParenthesis(): this;
85
+ /**
86
+ * Adds parenthesis on start and end, and a seperator to the output on
87
+ * each iteration
88
+ */
28
89
  call<T>(parts: Array<T>, separator?: string): Generator<T, this | undefined, unknown>;
90
+ /**
91
+ * Adds seperator to the output on each iteration
92
+ */
29
93
  separate<T>(parts: Array<T>, separator?: string): Generator<T, void, unknown>;
94
+ /**
95
+ * Returns true if the statement is empty
96
+ */
30
97
  isEmpty(): boolean;
98
+ /**
99
+ * Returns the parameter values in the order they appear in the query string
100
+ */
31
101
  params(input?: Record<string, any>): Array<any>;
102
+ /**
103
+ * For debug purposes this will apply parameter values to the query string,
104
+ * and return the result
105
+ */
106
+ inlineParams(): string;
32
107
  toString(): string;
33
108
  }
@@ -11,66 +11,120 @@ class Statement {
11
11
  sql = "";
12
12
  paramData = [];
13
13
  currentIndent = "";
14
- space() {
15
- if (this.sql === "")
14
+ /**
15
+ * UNSAFE
16
+ * Adds a string without applying escaping
17
+ */
18
+ raw(query) {
19
+ if (!query)
16
20
  return this;
17
- return this.raw(WHITESPACE);
21
+ this.sql += query;
22
+ return this;
18
23
  }
24
+ /**
25
+ * UNSAFE
26
+ * Adds a space if the statement is not empty, and then adds the given string
27
+ * without applying escaping
28
+ */
19
29
  add(addition) {
20
30
  if (!addition)
21
31
  return this;
22
32
  return this.space().raw(addition);
23
33
  }
34
+ /**
35
+ * UNSAFE
36
+ * Adds a newline if the statement is not empty, and then adds the given
37
+ * string without applying escaping
38
+ */
24
39
  addLine(addition) {
25
40
  if (!addition)
26
41
  return this;
27
42
  return this.newline().raw(addition);
28
43
  }
44
+ /**
45
+ * Adds a space if the statement is not empty
46
+ */
47
+ space() {
48
+ if (this.sql === "")
49
+ return this;
50
+ return this.raw(WHITESPACE);
51
+ }
52
+ /**
53
+ * Increases the indentation level
54
+ */
29
55
  indent() {
30
56
  this.currentIndent = this.currentIndent + " ";
31
57
  return this;
32
58
  }
59
+ /**
60
+ * Decreases the indentation level
61
+ */
33
62
  dedent() {
34
63
  this.currentIndent = this.currentIndent.slice(0, -2);
35
64
  return this;
36
65
  }
66
+ /**
67
+ * Adds a newline
68
+ */
37
69
  newline() {
38
70
  if (this.options.skipNewlines)
39
71
  return this;
40
72
  return this.raw(NEWLINE + this.currentIndent);
41
73
  }
74
+ /**
75
+ * Adds an identifier, applies escaping
76
+ */
42
77
  identifier(name) {
43
78
  return this.raw(this.sanitizer.escapeIdentifier(name));
44
79
  }
80
+ /**
81
+ * Adds a space if the statement is not empty, and then adds an identifier
82
+ */
45
83
  addIdentifier(name) {
46
84
  return this.space().identifier(name);
47
85
  }
86
+ /**
87
+ * Adds a value, applies escaping
88
+ */
48
89
  value(value) {
49
90
  this.paramData.push(new ParamData.Value(value));
50
91
  return this.raw(INSERT_PARAM);
51
92
  }
93
+ /**
94
+ * Adds a space if the statement is not empty, and then adds a value
95
+ */
52
96
  addValue(value) {
53
97
  return this.space().value(value);
54
98
  }
99
+ /**
100
+ * Adds a parameter
101
+ */
55
102
  param(data) {
56
103
  this.paramData.push(data);
57
104
  return this.raw(INSERT_PARAM);
58
105
  }
106
+ /**
107
+ * Adds a space if the statement is not empty, and then adds a parameter
108
+ */
59
109
  addParam(data) {
60
110
  return this.space().param(data);
61
111
  }
62
- raw(query) {
63
- if (!query)
64
- return this;
65
- this.sql += query;
66
- return this;
67
- }
112
+ /**
113
+ * Opens a parenthesis and indents the next line
114
+ */
68
115
  openParenthesis() {
69
116
  return this.raw("(").indent().newline();
70
117
  }
118
+ /**
119
+ * Closes a parenthesis and dedents the next line
120
+ */
71
121
  closeParenthesis() {
72
122
  return this.dedent().newline().raw(")");
73
123
  }
124
+ /**
125
+ * Adds parenthesis on start and end, and a seperator to the output on
126
+ * each iteration
127
+ */
74
128
  *call(parts, separator = SEPARATE) {
75
129
  if (parts.length === 0)
76
130
  return this.raw("()");
@@ -78,6 +132,9 @@ class Statement {
78
132
  yield* this.separate(parts, separator);
79
133
  this.closeParenthesis();
80
134
  }
135
+ /**
136
+ * Adds seperator to the output on each iteration
137
+ */
81
138
  *separate(parts, separator = SEPARATE) {
82
139
  for (let i = 0; i < parts.length; i++) {
83
140
  if (i > 0)
@@ -85,9 +142,15 @@ class Statement {
85
142
  yield parts[i];
86
143
  }
87
144
  }
145
+ /**
146
+ * Returns true if the statement is empty
147
+ */
88
148
  isEmpty() {
89
149
  return this.sql === "";
90
150
  }
151
+ /**
152
+ * Returns the parameter values in the order they appear in the query string
153
+ */
91
154
  params(input) {
92
155
  return this.paramData.map((param) => {
93
156
  if (param.type === ParamType.Named) {
@@ -98,6 +161,20 @@ class Statement {
98
161
  return this.sanitizer.formatParamValue(param.value);
99
162
  });
100
163
  }
164
+ /**
165
+ * For debug purposes this will apply parameter values to the query string,
166
+ * and return the result
167
+ */
168
+ inlineParams() {
169
+ let index = 0;
170
+ return this.sql.replace(/\?/g, () => {
171
+ const param = this.paramData[index];
172
+ index++;
173
+ if (param.type === ParamType.Named)
174
+ throw new TypeError(`Missing parameter ${param.name}`);
175
+ return this.sanitizer.escapeValue(param.value);
176
+ });
177
+ }
101
178
  toString() {
102
179
  return this.sql;
103
180
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rado",
3
- "version": "0.2.36",
3
+ "version": "0.2.39",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -59,7 +59,7 @@
59
59
  "sade": "^1.8.1",
60
60
  "speedscope": "^1.15.0",
61
61
  "sql.js": "^1.8.0",
62
- "sqlite3": "^5.1.4",
62
+ "sqlite3": "^5.1.6",
63
63
  "tsx": "^3.12.3",
64
64
  "typescript": "beta",
65
65
  "uvu": "^0.5.6"