@deterministic-code/datasources-sqlite 0.1.0 → 0.1.2
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/dist/SqliteAlterTableStatementRenderer.d.ts +5 -0
- package/dist/SqliteAlterTableStatementRenderer.js +27 -0
- package/dist/SqliteCreateTableStatementRenderer.d.ts +6 -0
- package/dist/SqliteCreateTableStatementRenderer.js +18 -0
- package/dist/SqliteCreateTriggerStatementRenderer.d.ts +4 -0
- package/dist/SqliteCreateTriggerStatementRenderer.js +12 -0
- package/dist/SqliteDriver.d.ts +16 -0
- package/dist/SqliteDriver.js +64 -0
- package/dist/SqliteStatementRenderer.d.ts +8 -0
- package/dist/SqliteStatementRenderer.js +26 -0
- package/dist/columnTypes.d.ts +1 -0
- package/dist/columnTypes.js +10 -0
- package/dist/command.d.ts +13 -0
- package/dist/command.js +163 -0
- package/dist/connection-options.d.ts +6 -0
- package/dist/connection-options.js +1 -0
- package/dist/connection.d.ts +20 -0
- package/dist/connection.js +58 -0
- package/dist/converters/BinaryConverter.d.ts +5 -0
- package/dist/converters/BinaryConverter.js +5 -0
- package/dist/converters/BoolConverter.d.ts +5 -0
- package/dist/converters/BoolConverter.js +5 -0
- package/dist/converters/DateTimeConverter.d.ts +5 -0
- package/dist/converters/DateTimeConverter.js +5 -0
- package/dist/converters/DecimalConverter.d.ts +5 -0
- package/dist/converters/DecimalConverter.js +5 -0
- package/dist/converters/SqliteRowTypeConverters.d.ts +3 -0
- package/dist/converters/SqliteRowTypeConverters.js +19 -0
- package/dist/converters/UuidConverter.d.ts +5 -0
- package/dist/converters/UuidConverter.js +5 -0
- package/dist/i-sqlite-driver.d.ts +17 -0
- package/dist/i-sqlite-driver.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/sqlitePath.d.ts +5 -0
- package/dist/sqlitePath.js +7 -0
- package/dist/transaction.d.ts +9 -0
- package/dist/transaction.js +15 -0
- package/package.json +10 -7
- package/src/SqliteCreateTableStatementRenderer.ts +0 -24
- package/src/SqliteDriver.ts +0 -53
- package/src/SqliteStatementRenderer.ts +0 -28
- package/src/columnTypes.ts +0 -10
- package/src/command.ts +0 -231
- package/src/connection-options.ts +0 -7
- package/src/connection.ts +0 -83
- package/src/converters/BinaryConverter.ts +0 -6
- package/src/converters/BoolConverter.ts +0 -6
- package/src/converters/DateTimeConverter.ts +0 -8
- package/src/converters/DecimalConverter.ts +0 -7
- package/src/converters/SqliteRowTypeConverters.ts +0 -24
- package/src/converters/UuidConverter.ts +0 -6
- package/src/i-sqlite-driver.ts +0 -21
- package/src/index.ts +0 -2
- package/src/sqlitePath.ts +0 -8
- package/src/transaction.ts +0 -21
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { AlterTableStatementTextRenderer, type AlterAction, type Column } from "@deterministic-code/datasources";
|
|
2
|
+
export declare class SqliteAlterTableStatementRenderer extends AlterTableStatementTextRenderer {
|
|
3
|
+
protected typeName(column: Column): string;
|
|
4
|
+
protected actionSql(action: AlterAction): string;
|
|
5
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { AlterTableStatementTextRenderer, } from "@deterministic-code/datasources";
|
|
2
|
+
import { columnTypes } from "./columnTypes.js";
|
|
3
|
+
export class SqliteAlterTableStatementRenderer extends AlterTableStatementTextRenderer {
|
|
4
|
+
typeName(column) {
|
|
5
|
+
return columnTypes[column.type] ?? column.type;
|
|
6
|
+
}
|
|
7
|
+
actionSql(action) {
|
|
8
|
+
return ({
|
|
9
|
+
alterColumn: () => {
|
|
10
|
+
throw new Error("SQLite cannot ALTER COLUMN");
|
|
11
|
+
},
|
|
12
|
+
addConstraint: () => {
|
|
13
|
+
throw new Error("SQLite cannot ADD CONSTRAINT");
|
|
14
|
+
},
|
|
15
|
+
dropConstraint: () => {
|
|
16
|
+
throw new Error("SQLite cannot DROP CONSTRAINT");
|
|
17
|
+
},
|
|
18
|
+
addColumn: () => super.actionSql(action),
|
|
19
|
+
dropColumn: () => super.actionSql(action),
|
|
20
|
+
renameColumn: () => super.actionSql(action),
|
|
21
|
+
renameTable: () => super.actionSql(action),
|
|
22
|
+
}[action.kind] ??
|
|
23
|
+
(() => {
|
|
24
|
+
throw new Error("empty alter");
|
|
25
|
+
}))();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { CreateTableStatement, CreateTableStatementTextRenderer, type Column } from "@deterministic-code/datasources";
|
|
2
|
+
export declare class SqliteCreateTableStatementRenderer extends CreateTableStatementTextRenderer {
|
|
3
|
+
render(statement: CreateTableStatement): string;
|
|
4
|
+
protected typeName(column: Column): string;
|
|
5
|
+
protected identity(column: Column): string;
|
|
6
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { CreateTableStatement, CreateTableStatementTextRenderer } from "@deterministic-code/datasources";
|
|
2
|
+
import { columnTypes } from "./columnTypes.js";
|
|
3
|
+
const primaryKey = (constraint) => constraint.kind === "primaryKey";
|
|
4
|
+
export class SqliteCreateTableStatementRenderer extends CreateTableStatementTextRenderer {
|
|
5
|
+
render(statement) {
|
|
6
|
+
const identity = new Set(statement.columns.filter((column) => column.identity).map((column) => column.name));
|
|
7
|
+
const constraints = statement.constraints.filter((constraint) => !primaryKey(constraint) || !constraint.columns.every((column) => identity.has(column)));
|
|
8
|
+
return super.render(constraints.length === statement.constraints.length
|
|
9
|
+
? statement
|
|
10
|
+
: new CreateTableStatement(statement.table, statement.columns, constraints));
|
|
11
|
+
}
|
|
12
|
+
typeName(column) {
|
|
13
|
+
return columnTypes[column.type] ?? column.type;
|
|
14
|
+
}
|
|
15
|
+
identity(column) {
|
|
16
|
+
return column.identity ? " PRIMARY KEY AUTOINCREMENT" : "";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { CreateTriggerStatementTextRenderer, type CreateTriggerStatement } from "@deterministic-code/datasources";
|
|
2
|
+
export declare class SqliteCreateTriggerStatementRenderer extends CreateTriggerStatementTextRenderer {
|
|
3
|
+
render(statement: CreateTriggerStatement): string;
|
|
4
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { CreateTriggerStatementTextRenderer } from "@deterministic-code/datasources";
|
|
2
|
+
export class SqliteCreateTriggerStatementRenderer extends CreateTriggerStatementTextRenderer {
|
|
3
|
+
render(statement) {
|
|
4
|
+
return `CREATE TRIGGER ${statement.name}
|
|
5
|
+
AFTER UPDATE ON ${statement.table}
|
|
6
|
+
FOR EACH ROW
|
|
7
|
+
WHEN NEW.${statement.updatedColumn} IS OLD.${statement.updatedColumn}
|
|
8
|
+
BEGIN
|
|
9
|
+
UPDATE ${statement.table} SET ${statement.updatedColumn} = ${statement.utcNow} WHERE ${statement.keyColumn} = OLD.${statement.keyColumn};
|
|
10
|
+
END`;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { DefaultValueConverter, DeterministicTypeConverter, StatementTextRenderer, type Cell, type DatasourceOptions, type ICommand, type IConnection, type IDriver, type Information, type ITransaction, type IsolationLevel, type TypeConverter } from "@deterministic-code/datasources";
|
|
2
|
+
import { columnTypes } from "./columnTypes.js";
|
|
3
|
+
export { columnTypes };
|
|
4
|
+
export declare class SqliteDriver implements IDriver {
|
|
5
|
+
readonly statementRenderer: StatementTextRenderer;
|
|
6
|
+
readonly information: Information;
|
|
7
|
+
rowTypeConverters: Record<string, TypeConverter<Cell, Cell>>;
|
|
8
|
+
readonly quoteLeft = "\"";
|
|
9
|
+
readonly quoteRight = "\"";
|
|
10
|
+
readonly typeConverter: DeterministicTypeConverter;
|
|
11
|
+
readonly defaultValueConverter: DefaultValueConverter;
|
|
12
|
+
columnType: (name: string) => string;
|
|
13
|
+
connection: (options?: DatasourceOptions) => IConnection;
|
|
14
|
+
command: (connection: IConnection) => ICommand;
|
|
15
|
+
transaction: (connection: IConnection, isolation?: IsolationLevel) => ITransaction;
|
|
16
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { DatasourceError, DefaultValueConverter, DeterministicTypeConverter, StatementTextRenderer, } from "@deterministic-code/datasources";
|
|
2
|
+
import { SqliteConnection } from "./connection.js";
|
|
3
|
+
import { SqliteCommand } from "./command.js";
|
|
4
|
+
import { SqliteTransaction } from "./transaction.js";
|
|
5
|
+
import { SqliteRowTypeConverters } from "./converters/SqliteRowTypeConverters.js";
|
|
6
|
+
import { SqliteStatementRenderer } from "./SqliteStatementRenderer.js";
|
|
7
|
+
import { columnTypes } from "./columnTypes.js";
|
|
8
|
+
export { columnTypes };
|
|
9
|
+
const statements = new StatementTextRenderer(t => new SqliteStatementRenderer(t));
|
|
10
|
+
export class SqliteDriver {
|
|
11
|
+
statementRenderer = statements;
|
|
12
|
+
information = {
|
|
13
|
+
descriptions: [
|
|
14
|
+
"Embedded SQL database for local development and tests.",
|
|
15
|
+
"Full connection, command, transaction, and repository support.",
|
|
16
|
+
],
|
|
17
|
+
overrides: [
|
|
18
|
+
"Overrides SelectInsertedIdStatement to SELECT last_insert_rowid() AS {column}",
|
|
19
|
+
"Overrides CreateTableStatement type mapping (int→INTEGER, bool→BOOLEAN, uuid/string/datetime→TEXT, decimal→NUMERIC, float→REAL, binary→BLOB)",
|
|
20
|
+
],
|
|
21
|
+
changeLogs: [],
|
|
22
|
+
};
|
|
23
|
+
rowTypeConverters = SqliteRowTypeConverters;
|
|
24
|
+
quoteLeft = '"';
|
|
25
|
+
quoteRight = '"';
|
|
26
|
+
typeConverter = new DeterministicTypeConverter({
|
|
27
|
+
string: "TEXT",
|
|
28
|
+
character: "TEXT",
|
|
29
|
+
number: "INTEGER",
|
|
30
|
+
integer: "INTEGER",
|
|
31
|
+
unsignedinteger: "INTEGER",
|
|
32
|
+
smallinteger: "INTEGER",
|
|
33
|
+
unsignedsmallinteger: "INTEGER",
|
|
34
|
+
biginteger: "INTEGER",
|
|
35
|
+
unsignedbiginteger: "INTEGER",
|
|
36
|
+
float: "REAL",
|
|
37
|
+
decimal: "NUMERIC",
|
|
38
|
+
boolean: "BOOLEAN",
|
|
39
|
+
datetime: "TEXT",
|
|
40
|
+
binary: "BLOB",
|
|
41
|
+
uuid: "TEXT",
|
|
42
|
+
reference: "INTEGER",
|
|
43
|
+
}, {
|
|
44
|
+
dialectName: "sqlite",
|
|
45
|
+
stringUnlimited: "TEXT",
|
|
46
|
+
stringSized: (n) => `VARCHAR(${n})`,
|
|
47
|
+
});
|
|
48
|
+
defaultValueConverter = new DefaultValueConverter({
|
|
49
|
+
newId: () => "",
|
|
50
|
+
now: () => `(strftime('%Y-%m-%dT%H:%M:%f', 'now', 'localtime'))`,
|
|
51
|
+
utcNow: () => `(strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))`,
|
|
52
|
+
booleanOn: "1",
|
|
53
|
+
booleanOff: "0",
|
|
54
|
+
hex: (arg) => `X'${arg}'`,
|
|
55
|
+
hexReverse: /^X'(.*)'$/,
|
|
56
|
+
});
|
|
57
|
+
columnType = (name) => columnTypes[name] ??
|
|
58
|
+
(() => {
|
|
59
|
+
throw new DatasourceError(`Unknown sqlite type "${name}"`);
|
|
60
|
+
})();
|
|
61
|
+
connection = (options) => SqliteConnection.from(options);
|
|
62
|
+
command = (connection) => new SqliteCommand(connection, statements);
|
|
63
|
+
transaction = (connection, isolation = "readCommitted") => new SqliteTransaction(connection, isolation);
|
|
64
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AlterTableStatement, CreateTableStatement, CreateTriggerStatement, SelectInsertedIdStatement, StatementRenderer, type StatementTextRenderer } from "@deterministic-code/datasources";
|
|
2
|
+
export declare class SqliteStatementRenderer extends StatementRenderer {
|
|
3
|
+
constructor(text: StatementTextRenderer);
|
|
4
|
+
protected visitSelectInsertedId(inserted: SelectInsertedIdStatement): string;
|
|
5
|
+
protected visitCreateTable(create: CreateTableStatement): string;
|
|
6
|
+
protected visitAlterTable(alter: AlterTableStatement): string;
|
|
7
|
+
protected visitCreateTrigger(trigger: CreateTriggerStatement): string;
|
|
8
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { SelectInsertedIdStatementTextRenderer, StatementRenderer, } from "@deterministic-code/datasources";
|
|
2
|
+
import { SqliteAlterTableStatementRenderer } from "./SqliteAlterTableStatementRenderer.js";
|
|
3
|
+
import { SqliteCreateTableStatementRenderer } from "./SqliteCreateTableStatementRenderer.js";
|
|
4
|
+
import { SqliteCreateTriggerStatementRenderer } from "./SqliteCreateTriggerStatementRenderer.js";
|
|
5
|
+
class SqliteSelectInsertedIdStatementTextRenderer extends SelectInsertedIdStatementTextRenderer {
|
|
6
|
+
render(statement) {
|
|
7
|
+
return `SELECT last_insert_rowid() AS ${statement.column}`;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export class SqliteStatementRenderer extends StatementRenderer {
|
|
11
|
+
constructor(text) {
|
|
12
|
+
super(text);
|
|
13
|
+
}
|
|
14
|
+
visitSelectInsertedId(inserted) {
|
|
15
|
+
return new SqliteSelectInsertedIdStatementTextRenderer().render(inserted);
|
|
16
|
+
}
|
|
17
|
+
visitCreateTable(create) {
|
|
18
|
+
return new SqliteCreateTableStatementRenderer().render(create);
|
|
19
|
+
}
|
|
20
|
+
visitAlterTable(alter) {
|
|
21
|
+
return new SqliteAlterTableStatementRenderer().renderAlter(alter);
|
|
22
|
+
}
|
|
23
|
+
visitCreateTrigger(trigger) {
|
|
24
|
+
return new SqliteCreateTriggerStatementRenderer().render(trigger);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const columnTypes: Record<string, string>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { CommandMiddlewareHost, Native, StatementTextRenderer, Response, type Cells, type ICommand, type IReader, type IStatement, type Parameter } from "@deterministic-code/datasources";
|
|
2
|
+
import type { SqliteConnection } from "./connection.js";
|
|
3
|
+
export declare class SqliteCommand extends CommandMiddlewareHost implements ICommand {
|
|
4
|
+
readonly connection: SqliteConnection;
|
|
5
|
+
private readonly statements;
|
|
6
|
+
constructor(connection: SqliteConnection, statements?: StatementTextRenderer);
|
|
7
|
+
executeReaderNative(text: Native, parameters?: Parameter[]): Promise<IReader<Cells>>;
|
|
8
|
+
executeReaderStatement(statement: IStatement, parameters?: Parameter[]): Promise<IReader<Cells>>;
|
|
9
|
+
executeStatement(statement: IStatement, parameters?: Parameter[]): Promise<Response>;
|
|
10
|
+
private firstCell;
|
|
11
|
+
executeNative(text: Native, parameters?: Parameter[]): Promise<Response>;
|
|
12
|
+
private iterate;
|
|
13
|
+
}
|
package/dist/command.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { ArrayReader, CommandMiddlewareHost, FirstResultReader, DatasourceError, Native, ReturnStatement, SelectStatement, StatementTextRenderer, parametersToRecord, Response, splitStatements, statementParameters, StreamingReader, } from "@deterministic-code/datasources";
|
|
2
|
+
import { reverse, SqliteRowTypeConverters } from "./converters/SqliteRowTypeConverters.js";
|
|
3
|
+
import { SqliteStatementRenderer } from "./SqliteStatementRenderer.js";
|
|
4
|
+
const bound = (parameters) => {
|
|
5
|
+
const record = parametersToRecord(parameters);
|
|
6
|
+
return Object.fromEntries(Object.entries(record).map(([name, value]) => [name, reverse(value)]));
|
|
7
|
+
};
|
|
8
|
+
const toSqlite = (sql, params) => {
|
|
9
|
+
const values = [];
|
|
10
|
+
const text = sql.replace(/\$([A-Za-z_][A-Za-z0-9_]*)/g, (_match, name) => {
|
|
11
|
+
values.push(params[name] ?? null);
|
|
12
|
+
return "?";
|
|
13
|
+
});
|
|
14
|
+
return { text, values };
|
|
15
|
+
};
|
|
16
|
+
export class SqliteCommand extends CommandMiddlewareHost {
|
|
17
|
+
connection;
|
|
18
|
+
statements;
|
|
19
|
+
constructor(connection, statements) {
|
|
20
|
+
super();
|
|
21
|
+
this.connection = connection;
|
|
22
|
+
this.statements = statements ?? new StatementTextRenderer(t => new SqliteStatementRenderer(t));
|
|
23
|
+
}
|
|
24
|
+
async executeReaderNative(text, parameters) {
|
|
25
|
+
return this.run(text.text, parameters, async (query, params) => {
|
|
26
|
+
if (!query.trim())
|
|
27
|
+
return { value: new ArrayReader([]), rows: [] };
|
|
28
|
+
this.connection.ensureOpen();
|
|
29
|
+
const statements = splitStatements(query);
|
|
30
|
+
if (statements.length === 0)
|
|
31
|
+
return { value: new ArrayReader([]), rows: [] };
|
|
32
|
+
const paramsBound = bound(params);
|
|
33
|
+
let index = 0;
|
|
34
|
+
let iter = this.iterate(statements[0], paramsBound);
|
|
35
|
+
return FirstResultReader.capture(new StreamingReader(async () => {
|
|
36
|
+
const step = iter.next();
|
|
37
|
+
return step.done ? undefined : step.value;
|
|
38
|
+
}, async () => {
|
|
39
|
+
iter.return();
|
|
40
|
+
index += 1;
|
|
41
|
+
const sql = statements[index];
|
|
42
|
+
if (!sql)
|
|
43
|
+
return false;
|
|
44
|
+
iter = this.iterate(sql, paramsBound);
|
|
45
|
+
return true;
|
|
46
|
+
}, async () => {
|
|
47
|
+
iter.return();
|
|
48
|
+
}));
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
async executeReaderStatement(statement, parameters) {
|
|
52
|
+
return this.executeReaderNative(new Native(this.statements.render(statement)), parameters);
|
|
53
|
+
}
|
|
54
|
+
async executeStatement(statement, parameters) {
|
|
55
|
+
return statement instanceof SelectStatement || statement instanceof ReturnStatement
|
|
56
|
+
? this.firstCell(statement, parameters)
|
|
57
|
+
: this.executeNative(new Native(this.statements.render(statement)), parameters);
|
|
58
|
+
}
|
|
59
|
+
async firstCell(statement, parameters) {
|
|
60
|
+
const reader = await this.executeReaderStatement(statement, parameters);
|
|
61
|
+
try {
|
|
62
|
+
if (!(await reader.next()))
|
|
63
|
+
return new Response(null, 0, parameters);
|
|
64
|
+
const row = reader.current;
|
|
65
|
+
/* v8 ignore next */
|
|
66
|
+
if (!row)
|
|
67
|
+
throw new DatasourceError("Reader current must be a row");
|
|
68
|
+
const values = Object.values(row);
|
|
69
|
+
return new Response(values.length === 0 ? null : values[0], 1, parameters);
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
await reader.dispose();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async executeNative(text, parameters) {
|
|
76
|
+
return this.run(text.text, parameters, async (query, params) => {
|
|
77
|
+
if (!query.trim())
|
|
78
|
+
return { value: new Response(undefined, 0, params), rows: [] };
|
|
79
|
+
this.connection.ensureOpen();
|
|
80
|
+
const db = this.connection.require();
|
|
81
|
+
const named = bound(params);
|
|
82
|
+
let changes = 0;
|
|
83
|
+
for (const sql of splitStatements(query)) {
|
|
84
|
+
const names = new Set(statementParameters(sql));
|
|
85
|
+
const used = Object.fromEntries(Object.entries(named).filter(([name]) => names.has(name.replace(/^[$:@]/, ""))));
|
|
86
|
+
const { text, values } = toSqlite(sql, used);
|
|
87
|
+
changes += db.prepare(text).run(...values).changes;
|
|
88
|
+
}
|
|
89
|
+
return { value: new Response(null, changes, params), rows: [] };
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
iterate(sql, params) {
|
|
93
|
+
const statement = this.connection.require().prepare(sql);
|
|
94
|
+
const columns = statement.columns?.() ?? [];
|
|
95
|
+
const names = qualified(columns);
|
|
96
|
+
const declared = columns.map((column) => column.type ?? null);
|
|
97
|
+
const raw = names.some((name, index) => name !== columns[index]?.name)
|
|
98
|
+
? statement.raw?.(true).iterate(params)
|
|
99
|
+
: undefined;
|
|
100
|
+
const rows = raw ? namedRows(raw, names, declared) : closable(statement.iterate(params), columns);
|
|
101
|
+
return rows[Symbol.iterator]();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const booleanDeclared = (declared) => declared?.toUpperCase() === "BOOLEAN";
|
|
105
|
+
const cell = (value, declared) => value == null ? null : booleanDeclared(declared) ? SqliteRowTypeConverters.bool.convert(value) : value;
|
|
106
|
+
const closable = (iter, columns) => {
|
|
107
|
+
const stop = iter.return ? iter.return.bind(iter) : undefined;
|
|
108
|
+
const rows = {
|
|
109
|
+
next: () => {
|
|
110
|
+
const step = iter.next();
|
|
111
|
+
if (step.done || !step.value)
|
|
112
|
+
return step;
|
|
113
|
+
const value = Object.fromEntries(Object.entries(step.value).map(([name, item]) => [
|
|
114
|
+
name,
|
|
115
|
+
cell(item, columns.find((column) => column.name === name)?.type),
|
|
116
|
+
]));
|
|
117
|
+
return { done: false, value };
|
|
118
|
+
},
|
|
119
|
+
return: () => {
|
|
120
|
+
if (stop)
|
|
121
|
+
stop();
|
|
122
|
+
return { done: true, value: undefined };
|
|
123
|
+
},
|
|
124
|
+
[Symbol.iterator]() {
|
|
125
|
+
return rows;
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
return rows;
|
|
129
|
+
};
|
|
130
|
+
const namedRows = (iter, names, declared) => {
|
|
131
|
+
/* v8 ignore next */
|
|
132
|
+
const stop = iter.return ? iter.return.bind(iter) : undefined;
|
|
133
|
+
const rows = {
|
|
134
|
+
next: () => {
|
|
135
|
+
const step = iter.next();
|
|
136
|
+
return step.done
|
|
137
|
+
? { done: true, value: undefined }
|
|
138
|
+
: {
|
|
139
|
+
done: false,
|
|
140
|
+
value: Object.fromEntries(names.map((name, index) => [name, cell(step.value[index], declared[index])])),
|
|
141
|
+
};
|
|
142
|
+
},
|
|
143
|
+
return: () => {
|
|
144
|
+
/* v8 ignore next */
|
|
145
|
+
if (stop)
|
|
146
|
+
stop();
|
|
147
|
+
return { done: true, value: undefined };
|
|
148
|
+
},
|
|
149
|
+
[Symbol.iterator]() {
|
|
150
|
+
return rows;
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
return rows;
|
|
154
|
+
};
|
|
155
|
+
const qualified = (columns) => {
|
|
156
|
+
const names = columns.map((column) => column.name);
|
|
157
|
+
const duplicated = new Set(names.filter((name, index) => names.indexOf(name) !== index));
|
|
158
|
+
return duplicated.size === 0
|
|
159
|
+
? names
|
|
160
|
+
: columns.map((column, index) => duplicated.has(names[index]) && column.table && column.column
|
|
161
|
+
? `${column.table}.${column.column}`
|
|
162
|
+
: names[index]);
|
|
163
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type DatasourceOptions, type ICommand, type IConnection, type IsolationLevelName, type ITransaction } from "@deterministic-code/datasources";
|
|
2
|
+
import type { SqliteConnectionOptions } from "./connection-options.js";
|
|
3
|
+
import type { SqliteDatabase } from "./i-sqlite-driver.js";
|
|
4
|
+
export declare class SqliteConnection implements IConnection {
|
|
5
|
+
readonly provider = "sqlite";
|
|
6
|
+
filename: string;
|
|
7
|
+
isOpen: boolean;
|
|
8
|
+
private db?;
|
|
9
|
+
private readonly Database?;
|
|
10
|
+
static from: (options?: DatasourceOptions) => SqliteConnection;
|
|
11
|
+
constructor(options?: SqliteConnectionOptions);
|
|
12
|
+
createCommand: () => ICommand;
|
|
13
|
+
open(): Promise<void>;
|
|
14
|
+
close(): Promise<void>;
|
|
15
|
+
[Symbol.asyncDispose]: () => Promise<void>;
|
|
16
|
+
startTransaction(isolation?: IsolationLevelName): Promise<ITransaction>;
|
|
17
|
+
ensureOpen(): void;
|
|
18
|
+
require(): SqliteDatabase;
|
|
19
|
+
exec(sql: string): void;
|
|
20
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { DatasourceError, IsolationLevel, StatementTextRenderer, } from "@deterministic-code/datasources";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { SqliteCommand } from "./command.js";
|
|
4
|
+
import { filenameOf } from "./sqlitePath.js";
|
|
5
|
+
import { SqliteStatementRenderer } from "./SqliteStatementRenderer.js";
|
|
6
|
+
import { SqliteTransaction } from "./transaction.js";
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
const pragma = (isolation) => isolation === IsolationLevel.ReadUncommitted
|
|
9
|
+
? "PRAGMA read_uncommitted = 1"
|
|
10
|
+
: "PRAGMA read_uncommitted = 0";
|
|
11
|
+
export class SqliteConnection {
|
|
12
|
+
provider = "sqlite";
|
|
13
|
+
filename;
|
|
14
|
+
isOpen = false;
|
|
15
|
+
db;
|
|
16
|
+
Database;
|
|
17
|
+
static from = (options) => new SqliteConnection({
|
|
18
|
+
url: options?.DATASOURCE_URL,
|
|
19
|
+
Database: require("better-sqlite3"),
|
|
20
|
+
});
|
|
21
|
+
constructor(options = {}) {
|
|
22
|
+
this.filename = filenameOf(options);
|
|
23
|
+
this.Database = options.Database;
|
|
24
|
+
}
|
|
25
|
+
createCommand = () => new SqliteCommand(this, new StatementTextRenderer(t => new SqliteStatementRenderer(t)));
|
|
26
|
+
async open() {
|
|
27
|
+
if (this.isOpen)
|
|
28
|
+
throw new DatasourceError("Connection is already open");
|
|
29
|
+
if (!this.Database)
|
|
30
|
+
throw new DatasourceError("SQLite driver is required");
|
|
31
|
+
this.db = new this.Database(this.filename, { timeout: 0 });
|
|
32
|
+
this.isOpen = true;
|
|
33
|
+
}
|
|
34
|
+
async close() {
|
|
35
|
+
this.db?.close();
|
|
36
|
+
this.db = undefined;
|
|
37
|
+
this.isOpen = false;
|
|
38
|
+
}
|
|
39
|
+
[Symbol.asyncDispose] = () => this.close();
|
|
40
|
+
async startTransaction(isolation = IsolationLevel.Serializable) {
|
|
41
|
+
this.ensureOpen();
|
|
42
|
+
this.exec(pragma(isolation));
|
|
43
|
+
this.exec("BEGIN");
|
|
44
|
+
return new SqliteTransaction(this, isolation);
|
|
45
|
+
}
|
|
46
|
+
ensureOpen() {
|
|
47
|
+
if (!this.isOpen)
|
|
48
|
+
throw new DatasourceError("Connection is not open");
|
|
49
|
+
}
|
|
50
|
+
require() {
|
|
51
|
+
if (!this.db)
|
|
52
|
+
throw new DatasourceError("Connection is not open");
|
|
53
|
+
return this.db;
|
|
54
|
+
}
|
|
55
|
+
exec(sql) {
|
|
56
|
+
this.require().exec(sql);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { BinaryConverter } from "./BinaryConverter.js";
|
|
2
|
+
import { BoolConverter } from "./BoolConverter.js";
|
|
3
|
+
import { DateTimeConverter } from "./DateTimeConverter.js";
|
|
4
|
+
import { DecimalConverter } from "./DecimalConverter.js";
|
|
5
|
+
import { UuidConverter } from "./UuidConverter.js";
|
|
6
|
+
export const SqliteRowTypeConverters = {
|
|
7
|
+
bool: new BoolConverter(),
|
|
8
|
+
datetime: new DateTimeConverter(),
|
|
9
|
+
uuid: new UuidConverter(),
|
|
10
|
+
decimal: new DecimalConverter(),
|
|
11
|
+
binary: new BinaryConverter(),
|
|
12
|
+
};
|
|
13
|
+
export const reverse = (value) => typeof value === "boolean"
|
|
14
|
+
? SqliteRowTypeConverters.bool.reverseConvert(value)
|
|
15
|
+
: value instanceof Date
|
|
16
|
+
? SqliteRowTypeConverters.datetime.reverseConvert(value)
|
|
17
|
+
: value instanceof Uint8Array
|
|
18
|
+
? SqliteRowTypeConverters.binary.reverseConvert(value)
|
|
19
|
+
: value;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Cell, Cells, Scalar } from "@deterministic-code/datasources";
|
|
2
|
+
export interface SqliteStatement {
|
|
3
|
+
run(params?: Record<string, Scalar>): {
|
|
4
|
+
changes: number;
|
|
5
|
+
lastInsertRowid?: Cell;
|
|
6
|
+
};
|
|
7
|
+
all(params?: Record<string, Scalar>): Cells[];
|
|
8
|
+
iterate(params?: Record<string, Scalar>): IterableIterator<Cells>;
|
|
9
|
+
}
|
|
10
|
+
export interface SqliteDatabase {
|
|
11
|
+
prepare(sql: string): SqliteStatement;
|
|
12
|
+
exec(sql: string): void;
|
|
13
|
+
close(): void;
|
|
14
|
+
}
|
|
15
|
+
export type SqliteDatabaseCtor = new (filename: string, options?: {
|
|
16
|
+
timeout?: number;
|
|
17
|
+
}) => SqliteDatabase;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SqliteDriver } from "./SqliteDriver.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SqliteDriver } from "./SqliteDriver.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const sqliteFilename = (url) => {
|
|
2
|
+
if (!/^sqlite:/i.test(url))
|
|
3
|
+
return undefined;
|
|
4
|
+
const path = url.replace(/^sqlite:(?:\/\/\/|\/\/)?/i, "");
|
|
5
|
+
return path === "" || path === ":memory" || path === ":memory:" ? ":memory:" : path;
|
|
6
|
+
};
|
|
7
|
+
export const filenameOf = (options) => options.filename ?? (options.url ? sqliteFilename(options.url) ?? ":memory:" : ":memory:");
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ITransaction, type IsolationLevelName } from "@deterministic-code/datasources";
|
|
2
|
+
import type { SqliteConnection } from "./connection.js";
|
|
3
|
+
export declare class SqliteTransaction implements ITransaction {
|
|
4
|
+
readonly connection: SqliteConnection;
|
|
5
|
+
readonly isolation: IsolationLevelName;
|
|
6
|
+
constructor(connection: SqliteConnection, isolation?: IsolationLevelName);
|
|
7
|
+
commit(): Promise<void>;
|
|
8
|
+
abort(): Promise<void>;
|
|
9
|
+
}
|