@enbox/dwn-sql-store 0.0.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.
- package/LICENSE +201 -0
- package/README.md +178 -0
- package/dist/cjs/main.js +3784 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/src/data-store-sql.js +103 -0
- package/dist/esm/src/data-store-sql.js.map +1 -0
- package/dist/esm/src/dialect/dialect.js +2 -0
- package/dist/esm/src/dialect/dialect.js.map +1 -0
- package/dist/esm/src/dialect/mysql-dialect.js +40 -0
- package/dist/esm/src/dialect/mysql-dialect.js.map +1 -0
- package/dist/esm/src/dialect/postgres-dialect.js +26 -0
- package/dist/esm/src/dialect/postgres-dialect.js.map +1 -0
- package/dist/esm/src/dialect/sqlite-dialect.js +33 -0
- package/dist/esm/src/dialect/sqlite-dialect.js.map +1 -0
- package/dist/esm/src/event-log-sql.js +169 -0
- package/dist/esm/src/event-log-sql.js.map +1 -0
- package/dist/esm/src/main.js +9 -0
- package/dist/esm/src/main.js.map +1 -0
- package/dist/esm/src/message-store-sql.js +317 -0
- package/dist/esm/src/message-store-sql.js.map +1 -0
- package/dist/esm/src/resumable-task-store-sql.js +141 -0
- package/dist/esm/src/resumable-task-store-sql.js.map +1 -0
- package/dist/esm/src/types.js +2 -0
- package/dist/esm/src/types.js.map +1 -0
- package/dist/esm/src/utils/filter.js +125 -0
- package/dist/esm/src/utils/filter.js.map +1 -0
- package/dist/esm/src/utils/sanitize.js +92 -0
- package/dist/esm/src/utils/sanitize.js.map +1 -0
- package/dist/esm/src/utils/tags.js +38 -0
- package/dist/esm/src/utils/tags.js.map +1 -0
- package/dist/esm/src/utils/transaction.js +25 -0
- package/dist/esm/src/utils/transaction.js.map +1 -0
- package/dist/types/src/data-store-sql.d.ts +14 -0
- package/dist/types/src/data-store-sql.d.ts.map +1 -0
- package/dist/types/src/dialect/dialect.d.ts +40 -0
- package/dist/types/src/dialect/dialect.d.ts.map +1 -0
- package/dist/types/src/dialect/mysql-dialect.d.ts +18 -0
- package/dist/types/src/dialect/mysql-dialect.d.ts.map +1 -0
- package/dist/types/src/dialect/postgres-dialect.d.ts +12 -0
- package/dist/types/src/dialect/postgres-dialect.d.ts.map +1 -0
- package/dist/types/src/dialect/sqlite-dialect.d.ts +12 -0
- package/dist/types/src/dialect/sqlite-dialect.d.ts.map +1 -0
- package/dist/types/src/event-log-sql.d.ts +24 -0
- package/dist/types/src/event-log-sql.d.ts.map +1 -0
- package/dist/types/src/main.d.ts +9 -0
- package/dist/types/src/main.d.ts.map +1 -0
- package/dist/types/src/message-store-sql.d.ts +46 -0
- package/dist/types/src/message-store-sql.d.ts.map +1 -0
- package/dist/types/src/resumable-task-store-sql.d.ts +16 -0
- package/dist/types/src/resumable-task-store-sql.d.ts.map +1 -0
- package/dist/types/src/types.d.ts +100 -0
- package/dist/types/src/types.d.ts.map +1 -0
- package/dist/types/src/utils/filter.d.ts +13 -0
- package/dist/types/src/utils/filter.d.ts.map +1 -0
- package/dist/types/src/utils/sanitize.d.ts +28 -0
- package/dist/types/src/utils/sanitize.d.ts.map +1 -0
- package/dist/types/src/utils/tags.d.ts +20 -0
- package/dist/types/src/utils/tags.d.ts.map +1 -0
- package/dist/types/src/utils/transaction.d.ts +7 -0
- package/dist/types/src/utils/transaction.d.ts.map +1 -0
- package/package.json +91 -0
- package/src/data-store-sql.ts +148 -0
- package/src/dialect/dialect.ts +77 -0
- package/src/dialect/mysql-dialect.ts +86 -0
- package/src/dialect/postgres-dialect.ts +66 -0
- package/src/dialect/sqlite-dialect.ts +72 -0
- package/src/event-log-sql.ts +227 -0
- package/src/main.ts +8 -0
- package/src/message-store-sql.ts +440 -0
- package/src/resumable-task-store-sql.ts +174 -0
- package/src/types.ts +109 -0
- package/src/utils/filter.ts +136 -0
- package/src/utils/sanitize.ts +117 -0
- package/src/utils/tags.ts +46 -0
- package/src/utils/transaction.ts +28 -0
package/package.json
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@enbox/dwn-sql-store",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "SQL backed implementations of DWN MessageStore, DataStore, and EventLog",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"homepage": "https://github.com/enboxorg/enbox/tree/main/packages/dwn-sql-store#readme",
|
|
8
|
+
"bugs": "https://github.com/enboxorg/enbox/issues",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/enboxorg/enbox.git",
|
|
12
|
+
"directory": "packages/dwn-sql-store"
|
|
13
|
+
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"main": "./dist/cjs/main.js",
|
|
18
|
+
"module": "./dist/esm/src/main.js",
|
|
19
|
+
"types": "./dist/types/src/main.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
"import": "./dist/esm/src/main.js",
|
|
22
|
+
"require": "./dist/cjs/main.js",
|
|
23
|
+
"types": "./dist/types/src/main.d.ts"
|
|
24
|
+
},
|
|
25
|
+
"react-native": "./dist/esm/src/main.js",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@enbox/dwn-sdk-js": "workspace:*",
|
|
28
|
+
"@ipld/dag-cbor": "9.0.5",
|
|
29
|
+
"kysely": "0.26.3",
|
|
30
|
+
"multiformats": "12.0.1",
|
|
31
|
+
"readable-stream": "4.4.2"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/better-sqlite3": "7.6.4",
|
|
35
|
+
"@types/chai": "4.3.4",
|
|
36
|
+
"@types/chai-as-promised": "7.1.5",
|
|
37
|
+
"@types/express": "4.17.17",
|
|
38
|
+
"@types/mocha": "10.0.1",
|
|
39
|
+
"@types/node": "20.3.1",
|
|
40
|
+
"@types/pg": "8.10.2",
|
|
41
|
+
"@types/pg-cursor": "2.7.0",
|
|
42
|
+
"@types/readable-stream": "4.0.6",
|
|
43
|
+
"@types/supertest": "2.0.12",
|
|
44
|
+
"@typescript-eslint/eslint-plugin": "5.59.11",
|
|
45
|
+
"@typescript-eslint/parser": "5.59.11",
|
|
46
|
+
"better-sqlite3": "8.4.0",
|
|
47
|
+
"c8": "8.0.0",
|
|
48
|
+
"chai": "4.3.6",
|
|
49
|
+
"chai-as-promised": "7.1.1",
|
|
50
|
+
"esbuild": "^0.19.3",
|
|
51
|
+
"eslint": "8.42.0",
|
|
52
|
+
"mocha": "10.2.0",
|
|
53
|
+
"mysql2": "3.9.8",
|
|
54
|
+
"pg": "8.11.1",
|
|
55
|
+
"pg-cursor": "2.10.1",
|
|
56
|
+
"rimraf": "5.0.1",
|
|
57
|
+
"sinon": "^18.0.1",
|
|
58
|
+
"typescript": "5.0.4"
|
|
59
|
+
},
|
|
60
|
+
"scripts": {
|
|
61
|
+
"build:esm": "pnpm clean & tsc",
|
|
62
|
+
"build:cjs": "pnpm build:esm && node build/create-cjs-bundle.cjs && echo '{\"type\": \"commonjs\"}' > ./dist/cjs/package.json",
|
|
63
|
+
"build": "pnpm clean && pnpm build:esm && pnpm build:cjs",
|
|
64
|
+
"clean": "rimraf dist compiled",
|
|
65
|
+
"lint": "eslint . --ext .ts --max-warnings 0",
|
|
66
|
+
"lint:fix": "eslint . --ext .ts --fix",
|
|
67
|
+
"test": "rimraf compiled && tsc -p tests/tsconfig.json && mocha",
|
|
68
|
+
"test-coverage": "rimraf compiled && tsc -p tests/tsconfig.json && c8 mocha"
|
|
69
|
+
},
|
|
70
|
+
"files": [
|
|
71
|
+
"dist",
|
|
72
|
+
"src"
|
|
73
|
+
],
|
|
74
|
+
"engines": {
|
|
75
|
+
"node": ">=18"
|
|
76
|
+
},
|
|
77
|
+
"contributors": [
|
|
78
|
+
{
|
|
79
|
+
"name": "Adam Mika",
|
|
80
|
+
"url": "https://github.com/amika-sq"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"name": "Moe Jangda",
|
|
84
|
+
"url": "https://github.com/mistermoe"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"name": "Liran Cohen",
|
|
88
|
+
"url": "https://github.com/lirancohen"
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { DataStore, DataStream, DataStoreGetResult, DataStorePutResult } from '@enbox/dwn-sdk-js';
|
|
2
|
+
import { Kysely } from 'kysely';
|
|
3
|
+
import { Readable } from 'readable-stream';
|
|
4
|
+
import { DwnDatabaseType } from './types.js';
|
|
5
|
+
import { Dialect } from './dialect/dialect.js';
|
|
6
|
+
|
|
7
|
+
export class DataStoreSql implements DataStore {
|
|
8
|
+
#dialect: Dialect;
|
|
9
|
+
#db: Kysely<DwnDatabaseType> | null = null;
|
|
10
|
+
|
|
11
|
+
constructor(dialect: Dialect) {
|
|
12
|
+
this.#dialect = dialect;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async open(): Promise<void> {
|
|
16
|
+
if (this.#db) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
this.#db = new Kysely<DwnDatabaseType>({ dialect: this.#dialect });
|
|
21
|
+
|
|
22
|
+
// if table already exists, there is no more things todo
|
|
23
|
+
const tableName = 'dataStore';
|
|
24
|
+
const tableExists = await this.#dialect.hasTable(this.#db, tableName);
|
|
25
|
+
if (tableExists) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// else create the table and corresponding indexes
|
|
30
|
+
|
|
31
|
+
let table = this.#db.schema
|
|
32
|
+
.createTable(tableName)
|
|
33
|
+
.ifNotExists()// kept to show supported by all dialects in contrast to ifNotExists() below, though not needed due to hasTable() check above
|
|
34
|
+
.addColumn('tenant', 'varchar(255)', (col) => col.notNull())
|
|
35
|
+
.addColumn('recordId', 'varchar(60)', (col) => col.notNull())
|
|
36
|
+
.addColumn('dataCid', 'varchar(60)', (col) => col.notNull());
|
|
37
|
+
|
|
38
|
+
// Add columns that have dialect-specific constraints
|
|
39
|
+
table = this.#dialect.addAutoIncrementingColumn(table, 'id', (col) => col.primaryKey());
|
|
40
|
+
table = this.#dialect.addBlobColumn(table, 'data', (col) => col.notNull());
|
|
41
|
+
await table.execute();
|
|
42
|
+
|
|
43
|
+
// Add index for efficient lookups.
|
|
44
|
+
await this.#db.schema
|
|
45
|
+
.createIndex('tenant_recordId_dataCid')
|
|
46
|
+
// .ifNotExists() // intentionally kept commented out code to show that it is not supported by all dialects (ie. MySQL)
|
|
47
|
+
.on(tableName)
|
|
48
|
+
.columns(['tenant', 'recordId', 'dataCid'])
|
|
49
|
+
.unique()
|
|
50
|
+
.execute();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async close(): Promise<void> {
|
|
54
|
+
await this.#db?.destroy();
|
|
55
|
+
this.#db = null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async get(
|
|
59
|
+
tenant: string,
|
|
60
|
+
recordId: string,
|
|
61
|
+
dataCid: string
|
|
62
|
+
): Promise<DataStoreGetResult | undefined> {
|
|
63
|
+
if (!this.#db) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
'Connection to database not open. Call `open` before using `get`.'
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const result = await this.#db
|
|
70
|
+
.selectFrom('dataStore')
|
|
71
|
+
.selectAll()
|
|
72
|
+
.where('tenant', '=', tenant)
|
|
73
|
+
.where('recordId', '=', recordId)
|
|
74
|
+
.where('dataCid', '=', dataCid)
|
|
75
|
+
.executeTakeFirst();
|
|
76
|
+
|
|
77
|
+
if (!result) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
dataSize : result.data.length,
|
|
83
|
+
dataStream : new Readable({
|
|
84
|
+
read() {
|
|
85
|
+
this.push(Buffer.from(result.data));
|
|
86
|
+
this.push(null);
|
|
87
|
+
}
|
|
88
|
+
}),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async put(
|
|
93
|
+
tenant: string,
|
|
94
|
+
recordId: string,
|
|
95
|
+
dataCid: string,
|
|
96
|
+
dataStream: Readable
|
|
97
|
+
): Promise<DataStorePutResult> {
|
|
98
|
+
if (!this.#db) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
'Connection to database not open. Call `open` before using `put`.'
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const bytes = await DataStream.toBytes(dataStream);
|
|
105
|
+
const data = Buffer.from(bytes);
|
|
106
|
+
|
|
107
|
+
await this.#db
|
|
108
|
+
.insertInto('dataStore')
|
|
109
|
+
.values({ tenant, recordId, dataCid, data })
|
|
110
|
+
.executeTakeFirstOrThrow();
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
dataSize: bytes.length
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async delete(
|
|
118
|
+
tenant: string,
|
|
119
|
+
recordId: string,
|
|
120
|
+
dataCid: string
|
|
121
|
+
): Promise<void> {
|
|
122
|
+
if (!this.#db) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
'Connection to database not open. Call `open` before using `delete`.'
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
await this.#db
|
|
129
|
+
.deleteFrom('dataStore')
|
|
130
|
+
.where('tenant', '=', tenant)
|
|
131
|
+
.where('recordId', '=', recordId)
|
|
132
|
+
.where('dataCid', '=', dataCid)
|
|
133
|
+
.execute();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async clear(): Promise<void> {
|
|
137
|
+
if (!this.#db) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
'Connection to database not open. Call `open` before using `clear`.'
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
await this.#db
|
|
144
|
+
.deleteFrom('dataStore')
|
|
145
|
+
.execute();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ColumnBuilderCallback,
|
|
3
|
+
ColumnDataType,
|
|
4
|
+
CreateTableBuilder,
|
|
5
|
+
Dialect as KyselyDialect,
|
|
6
|
+
Kysely,
|
|
7
|
+
InsertObject,
|
|
8
|
+
InsertQueryBuilder,
|
|
9
|
+
Selection,
|
|
10
|
+
SelectExpression,
|
|
11
|
+
Transaction,
|
|
12
|
+
} from 'kysely';
|
|
13
|
+
|
|
14
|
+
export interface Dialect extends KyselyDialect {
|
|
15
|
+
readonly name: string;
|
|
16
|
+
readonly isStreamingSupported: boolean;
|
|
17
|
+
|
|
18
|
+
hasTable(db: Kysely<any>, tableName: string): Promise<boolean>;
|
|
19
|
+
|
|
20
|
+
addAutoIncrementingColumn<TB extends string>(
|
|
21
|
+
builder: CreateTableBuilder<TB>,
|
|
22
|
+
columnName: string,
|
|
23
|
+
callback?: ColumnBuilderCallback
|
|
24
|
+
): CreateTableBuilder<TB>;
|
|
25
|
+
|
|
26
|
+
addBlobColumn<TB extends string>(
|
|
27
|
+
builder: CreateTableBuilder<TB>,
|
|
28
|
+
columnName: string,
|
|
29
|
+
callback?: ColumnBuilderCallback
|
|
30
|
+
): CreateTableBuilder<TB>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* This is a helper method to add a column with foreign key constraints.
|
|
34
|
+
* This is primarily useful because the `mySQL` dialect adds the constraints in a different way than `sqlite` and `postgres`.
|
|
35
|
+
*
|
|
36
|
+
* @param builder the CreateTableBuilder to add the column to.
|
|
37
|
+
* @param tableName the name of the table to add the column to.
|
|
38
|
+
* @param columnName the name of the column to add.
|
|
39
|
+
* @param columnType the type of the column to add.
|
|
40
|
+
* @param referenceTable the foreign table to reference.
|
|
41
|
+
* @param referenceColumnName the foreign column to reference.
|
|
42
|
+
* @param onDeleteAction the action to take when the referenced row is deleted.
|
|
43
|
+
*
|
|
44
|
+
* @returns {CreateTableBuilder} the CreateTableBuilder with the added column.
|
|
45
|
+
*/
|
|
46
|
+
addReferencedColumn<TB extends string>(
|
|
47
|
+
builder: CreateTableBuilder<TB & string>,
|
|
48
|
+
tableName: TB,
|
|
49
|
+
columnName: string,
|
|
50
|
+
columnType: ColumnDataType,
|
|
51
|
+
referenceTable: string,
|
|
52
|
+
referenceColumnName: string,
|
|
53
|
+
onDeleteAction: 'cascade' | 'no action' | 'restrict' | 'set null' | 'set default',
|
|
54
|
+
): CreateTableBuilder<TB & string>;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* This is a helper method to return an `insertId` across all dialects after inserting values.
|
|
58
|
+
* `postgres` and `sqlite` both support the `returning` clause, however `mysql` does not and instead returns the last inserted id.
|
|
59
|
+
*
|
|
60
|
+
* @param db the Kysely DB object or a DB Transaction.
|
|
61
|
+
* @param table the table to insert into.
|
|
62
|
+
* @param values the values to insert.
|
|
63
|
+
* @param returning a string representing the generated key you'd like returned as an insertId.
|
|
64
|
+
*
|
|
65
|
+
* NOTE: the `returning` value must be formatted to return an insertId value.
|
|
66
|
+
* ex. if the generated key is `id` the string should be `id as insertId`.
|
|
67
|
+
* if the generated key is `watermark` the string should be `watermark as insertId`.
|
|
68
|
+
*
|
|
69
|
+
* @returns {InsertQueryBuilder} object to further modify the query or execute it.
|
|
70
|
+
*/
|
|
71
|
+
insertThenReturnId<DB, TB extends keyof DB = keyof DB, SE extends SelectExpression<DB, TB & string> = any>(
|
|
72
|
+
db: Transaction<DB> | Kysely<DB>,
|
|
73
|
+
table: TB & string,
|
|
74
|
+
values: InsertObject<DB, TB & string>,
|
|
75
|
+
returning: SE & `${string} as insertId`,
|
|
76
|
+
): InsertQueryBuilder<DB, TB & string, Selection<DB, TB & string, SE & `${string} as insertId`>>;
|
|
77
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Dialect } from './dialect.js';
|
|
2
|
+
import {
|
|
3
|
+
AnyColumn,
|
|
4
|
+
ColumnDataType,
|
|
5
|
+
CreateTableBuilder,
|
|
6
|
+
ColumnBuilderCallback,
|
|
7
|
+
InsertObject,
|
|
8
|
+
InsertQueryBuilder,
|
|
9
|
+
SelectExpression,
|
|
10
|
+
Selection,
|
|
11
|
+
Transaction,
|
|
12
|
+
MysqlDialect as KyselyMysqlDialect,
|
|
13
|
+
Kysely,
|
|
14
|
+
} from 'kysely';
|
|
15
|
+
|
|
16
|
+
export class MysqlDialect extends KyselyMysqlDialect implements Dialect {
|
|
17
|
+
name = 'MySQL';
|
|
18
|
+
isStreamingSupported = true;
|
|
19
|
+
|
|
20
|
+
async hasTable(db: Kysely<any>, tableName: string): Promise<boolean> {
|
|
21
|
+
const result = await db
|
|
22
|
+
.selectFrom('information_schema.tables')
|
|
23
|
+
.select('table_name')
|
|
24
|
+
.where('table_name', '=', tableName)
|
|
25
|
+
.execute();
|
|
26
|
+
|
|
27
|
+
return result.length > 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
addAutoIncrementingColumn<TB extends string>(
|
|
31
|
+
builder: CreateTableBuilder<TB>,
|
|
32
|
+
columnName: string,
|
|
33
|
+
callback?: ColumnBuilderCallback
|
|
34
|
+
): CreateTableBuilder<TB> {
|
|
35
|
+
return builder.addColumn(columnName, 'integer', (col) => {
|
|
36
|
+
col = col.autoIncrement();
|
|
37
|
+
if (callback) {
|
|
38
|
+
col = callback(col);
|
|
39
|
+
}
|
|
40
|
+
return col;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
addBlobColumn<TB extends string>(
|
|
45
|
+
builder: CreateTableBuilder<TB>,
|
|
46
|
+
columnName: string,
|
|
47
|
+
callback?: ColumnBuilderCallback
|
|
48
|
+
): CreateTableBuilder<TB> {
|
|
49
|
+
return builder.addColumn(columnName, 'blob', callback);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* In MySQL, the ForeignKey name it creates in `mysql` will be in the following format:
|
|
54
|
+
* `${referenceTable}_${referenceColumnName}__${tableName}_${columnName}`
|
|
55
|
+
* ex: if the reference table is `users` and the reference column is `id` and the table is `profiles` and the column is `userId`,
|
|
56
|
+
* the resulting name for the foreign key is: `users_id__profiles_userId`
|
|
57
|
+
*/
|
|
58
|
+
addReferencedColumn<TB extends string>(
|
|
59
|
+
builder: CreateTableBuilder<TB & string>,
|
|
60
|
+
tableName: TB,
|
|
61
|
+
columnName: string,
|
|
62
|
+
columnType: ColumnDataType,
|
|
63
|
+
referenceTable: string,
|
|
64
|
+
referenceColumnName: string,
|
|
65
|
+
onDeleteAction: 'cascade' | 'no action' | 'restrict' | 'set null' | 'set default',
|
|
66
|
+
): CreateTableBuilder<TB & string> {
|
|
67
|
+
return builder
|
|
68
|
+
.addColumn(columnName, columnType, (col) => col.notNull())
|
|
69
|
+
.addForeignKeyConstraint(
|
|
70
|
+
`${referenceTable}_${referenceColumnName}__${tableName}_${columnName}`,
|
|
71
|
+
[columnName],
|
|
72
|
+
referenceTable,
|
|
73
|
+
[referenceColumnName],
|
|
74
|
+
(constraint) => constraint.onDelete(onDeleteAction)
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
insertThenReturnId<DB, TB extends keyof DB = keyof DB, SE extends SelectExpression<DB, TB & string> = AnyColumn<DB, TB>>(
|
|
79
|
+
db: Transaction<DB> | Kysely<DB>,
|
|
80
|
+
table: TB & string,
|
|
81
|
+
values: InsertObject<DB, TB & string>,
|
|
82
|
+
_returning: SE & `${string} as insertId`,
|
|
83
|
+
): InsertQueryBuilder<DB, TB & string, Selection<DB, TB & string, SE & `${string} as insertId`>> {
|
|
84
|
+
return db.insertInto(table).values(values);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Dialect } from './dialect.js';
|
|
2
|
+
import {
|
|
3
|
+
ColumnDataType,
|
|
4
|
+
ColumnBuilderCallback,
|
|
5
|
+
CreateTableBuilder,
|
|
6
|
+
InsertObject,
|
|
7
|
+
InsertQueryBuilder,
|
|
8
|
+
Kysely,
|
|
9
|
+
PostgresDialect as KyselyPostgresDialect,
|
|
10
|
+
SelectExpression,
|
|
11
|
+
Selection,
|
|
12
|
+
Transaction,
|
|
13
|
+
} from 'kysely';
|
|
14
|
+
|
|
15
|
+
export class PostgresDialect extends KyselyPostgresDialect implements Dialect {
|
|
16
|
+
name = 'PostgreSQL';
|
|
17
|
+
isStreamingSupported = true;
|
|
18
|
+
|
|
19
|
+
async hasTable(db: Kysely<any>, tableName: string): Promise<boolean> {
|
|
20
|
+
const result = await db
|
|
21
|
+
.selectFrom('information_schema.tables')
|
|
22
|
+
.select('table_name')
|
|
23
|
+
.where('table_name', '=', tableName)
|
|
24
|
+
.execute();
|
|
25
|
+
|
|
26
|
+
return result.length > 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
addAutoIncrementingColumn<TB extends string>(
|
|
30
|
+
builder: CreateTableBuilder<TB>,
|
|
31
|
+
columnName: string,
|
|
32
|
+
callback?: ColumnBuilderCallback
|
|
33
|
+
): CreateTableBuilder<TB> {
|
|
34
|
+
return builder.addColumn(columnName, 'serial', callback);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
addBlobColumn<TB extends string>(
|
|
38
|
+
builder: CreateTableBuilder<TB>,
|
|
39
|
+
columnName: string,
|
|
40
|
+
callback?: ColumnBuilderCallback
|
|
41
|
+
): CreateTableBuilder<TB> {
|
|
42
|
+
return builder.addColumn(columnName, 'bytea', callback);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
addReferencedColumn<TB extends string>(
|
|
46
|
+
builder: CreateTableBuilder<TB & string>,
|
|
47
|
+
_tableName: TB,
|
|
48
|
+
columnName: string,
|
|
49
|
+
columnType: ColumnDataType,
|
|
50
|
+
referenceTable: string,
|
|
51
|
+
referenceColumnName: string,
|
|
52
|
+
onDeleteAction: 'cascade' | 'no action' | 'restrict' | 'set null' | 'set default',
|
|
53
|
+
): CreateTableBuilder<TB & string> {
|
|
54
|
+
return builder.addColumn(columnName, columnType, (col) => col.notNull().references(`${referenceTable}.${referenceColumnName}`).onDelete(onDeleteAction));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
insertThenReturnId<DB, TB extends keyof DB = keyof DB, SE extends SelectExpression<DB, TB & string> = any>(
|
|
58
|
+
db: Transaction<DB> | Kysely<DB>,
|
|
59
|
+
table: TB & string,
|
|
60
|
+
values: InsertObject<DB, TB & string>,
|
|
61
|
+
returning: SE & `${string} as insertId`,
|
|
62
|
+
): InsertQueryBuilder<DB, TB & string, Selection<DB, TB & string, SE & `${string} as insertId`>> {
|
|
63
|
+
return db.insertInto(table).values(values).returning(returning);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Dialect } from './dialect.js';
|
|
2
|
+
import {
|
|
3
|
+
ColumnBuilderCallback,
|
|
4
|
+
ColumnDataType,
|
|
5
|
+
CreateTableBuilder,
|
|
6
|
+
Kysely,
|
|
7
|
+
InsertObject,
|
|
8
|
+
InsertQueryBuilder,
|
|
9
|
+
SelectExpression,
|
|
10
|
+
Selection,
|
|
11
|
+
SqliteDialect as KyselySqliteDialect,
|
|
12
|
+
Transaction,
|
|
13
|
+
} from 'kysely';
|
|
14
|
+
|
|
15
|
+
export class SqliteDialect extends KyselySqliteDialect implements Dialect {
|
|
16
|
+
name = 'SQLite';
|
|
17
|
+
isStreamingSupported = false;
|
|
18
|
+
|
|
19
|
+
async hasTable(db: Kysely<any>, tableName: string): Promise<boolean> {
|
|
20
|
+
const result = await db
|
|
21
|
+
.selectFrom('sqlite_master')
|
|
22
|
+
.select('name')
|
|
23
|
+
.where('type', '=', 'table')
|
|
24
|
+
.where('name', '=', tableName)
|
|
25
|
+
.execute();
|
|
26
|
+
|
|
27
|
+
return result.length > 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
addAutoIncrementingColumn<TB extends string>(
|
|
31
|
+
builder: CreateTableBuilder<TB>,
|
|
32
|
+
columnName: string,
|
|
33
|
+
callback?: ColumnBuilderCallback
|
|
34
|
+
): CreateTableBuilder<TB> {
|
|
35
|
+
return builder.addColumn(columnName, 'integer', (col) => {
|
|
36
|
+
col = col.autoIncrement();
|
|
37
|
+
if (callback) {
|
|
38
|
+
col = callback(col);
|
|
39
|
+
}
|
|
40
|
+
return col;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
addBlobColumn<TB extends string>(
|
|
45
|
+
builder: CreateTableBuilder<TB>,
|
|
46
|
+
columnName: string,
|
|
47
|
+
callback?: ColumnBuilderCallback
|
|
48
|
+
): CreateTableBuilder<TB> {
|
|
49
|
+
return builder.addColumn(columnName, 'blob', callback);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
addReferencedColumn<TB extends string>(
|
|
53
|
+
builder: CreateTableBuilder<TB & string>,
|
|
54
|
+
_tableName: TB,
|
|
55
|
+
columnName: string,
|
|
56
|
+
columnType: ColumnDataType,
|
|
57
|
+
referenceTable: string,
|
|
58
|
+
referenceColumnName: string,
|
|
59
|
+
onDeleteAction: 'cascade' | 'no action' | 'restrict' | 'set null' | 'set default',
|
|
60
|
+
): CreateTableBuilder<TB & string> {
|
|
61
|
+
return builder.addColumn(columnName, columnType, (col) => col.notNull().references(`${referenceTable}.${referenceColumnName}`).onDelete(onDeleteAction));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
insertThenReturnId<DB, TB extends keyof DB = keyof DB, SE extends SelectExpression<DB, TB & string> = any>(
|
|
65
|
+
db: Transaction<DB> | Kysely<DB>,
|
|
66
|
+
table: TB & string,
|
|
67
|
+
values: InsertObject<DB, TB & string>,
|
|
68
|
+
returning: SE & `${string} as insertId`,
|
|
69
|
+
): InsertQueryBuilder<DB, TB & string, Selection<DB, TB & string, SE & `${string} as insertId`>> {
|
|
70
|
+
return db.insertInto(table).values(values).returning(returning);
|
|
71
|
+
}
|
|
72
|
+
}
|