@undefineds.co/xpod 0.3.25 → 0.3.26
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/config/cloud.json +6 -5
- package/config/main.json +0 -3
- package/config/xpod.base.json +0 -32
- package/dist/api/container/index.js +3 -0
- package/dist/api/container/index.js.map +1 -1
- package/dist/api/container/routes.js +2 -0
- package/dist/api/container/routes.js.map +1 -1
- package/dist/api/container/types.d.ts +5 -0
- package/dist/api/container/types.js.map +1 -1
- package/dist/authorization/AuthMode.d.ts +8 -0
- package/dist/authorization/AuthMode.js +51 -0
- package/dist/authorization/AuthMode.js.map +1 -0
- package/dist/authorization/PodAuthorizationResources.d.ts +18 -0
- package/dist/authorization/PodAuthorizationResources.js +108 -0
- package/dist/authorization/PodAuthorizationResources.js.map +1 -0
- package/dist/cli/commands/start.js +11 -2
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/components/components.jsonld +3 -2
- package/dist/components/context.jsonld +115 -14
- package/dist/index.d.ts +5 -3
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/main.js +11 -2
- package/dist/main.js.map +1 -1
- package/dist/provision/LocalPodProvisioningService.d.ts +6 -2
- package/dist/provision/LocalPodProvisioningService.js +36 -33
- package/dist/provision/LocalPodProvisioningService.js.map +1 -1
- package/dist/provision/LocalPodProvisioningService.jsonld +65 -8
- package/dist/runtime/XpodRuntime.js +0 -1
- package/dist/runtime/XpodRuntime.js.map +1 -1
- package/dist/runtime/bootstrap.d.ts +4 -2
- package/dist/runtime/bootstrap.js +43 -11
- package/dist/runtime/bootstrap.js.map +1 -1
- package/dist/runtime/css-process.d.ts +6 -1
- package/dist/runtime/css-process.js +18 -6
- package/dist/runtime/css-process.js.map +1 -1
- package/dist/runtime/lifecycle.d.ts +2 -3
- package/dist/runtime/lifecycle.js +2 -2
- package/dist/runtime/lifecycle.js.map +1 -1
- package/dist/runtime/runtime-types.d.ts +2 -1
- package/dist/runtime/runtime-types.js.map +1 -1
- package/dist/storage/accessors/SolidRdfDataAccessor.d.ts +2 -3
- package/dist/storage/accessors/SolidRdfDataAccessor.js +48 -42
- package/dist/storage/accessors/SolidRdfDataAccessor.js.map +1 -1
- package/dist/storage/accessors/SolidRdfDataAccessor.jsonld +1 -1
- package/dist/storage/keyvalue/BaseKeyValueStorage.d.ts +33 -0
- package/dist/storage/keyvalue/BaseKeyValueStorage.js +106 -0
- package/dist/storage/keyvalue/BaseKeyValueStorage.js.map +1 -0
- package/dist/storage/keyvalue/BaseKeyValueStorage.jsonld +177 -0
- package/dist/storage/keyvalue/PostgresKeyValueStorage.d.ts +9 -18
- package/dist/storage/keyvalue/PostgresKeyValueStorage.js +24 -96
- package/dist/storage/keyvalue/PostgresKeyValueStorage.js.map +1 -1
- package/dist/storage/keyvalue/PostgresKeyValueStorage.jsonld +15 -58
- package/dist/storage/keyvalue/SqliteKeyValueStorage.d.ts +9 -15
- package/dist/storage/keyvalue/SqliteKeyValueStorage.js +36 -104
- package/dist/storage/keyvalue/SqliteKeyValueStorage.js.map +1 -1
- package/dist/storage/keyvalue/SqliteKeyValueStorage.jsonld +21 -52
- package/dist/storage/quint/BaseQuintStore.d.ts +4 -1
- package/dist/storage/quint/BaseQuintStore.js +59 -46
- package/dist/storage/quint/BaseQuintStore.js.map +1 -1
- package/dist/storage/quint/PgQuintStore.d.ts +4 -3
- package/dist/storage/quint/PgQuintStore.js.map +1 -1
- package/dist/storage/quint/SqliteQuintStore.d.ts +43 -54
- package/dist/storage/quint/SqliteQuintStore.js +197 -520
- package/dist/storage/quint/SqliteQuintStore.js.map +1 -1
- package/dist/storage/quint/SqliteQuintStore.jsonld +38 -86
- package/dist/storage/rdf/PostgresRdfEngine.d.ts +118 -0
- package/dist/storage/rdf/PostgresRdfEngine.js +2609 -0
- package/dist/storage/rdf/PostgresRdfEngine.js.map +1 -0
- package/dist/storage/rdf/PostgresRdfEngine.jsonld +657 -0
- package/dist/storage/rdf/SolidRdfEngine.d.ts +2 -2
- package/dist/storage/rdf/SolidRdfEngine.js.map +1 -1
- package/dist/storage/rdf/SolidRdfEngine.jsonld +3 -0
- package/dist/storage/rdf/SolidRdfSparqlEngine.d.ts +3 -3
- package/dist/storage/rdf/SolidRdfSparqlEngine.js +20 -20
- package/dist/storage/rdf/SolidRdfSparqlEngine.js.map +1 -1
- package/dist/storage/rdf/SolidRdfSparqlEngine.jsonld +1 -1
- package/dist/storage/rdf/index.d.ts +2 -1
- package/dist/storage/rdf/index.js +3 -1
- package/dist/storage/rdf/index.js.map +1 -1
- package/dist/storage/rdf/types.d.ts +19 -0
- package/dist/storage/rdf/types.js.map +1 -1
- package/dist/storage/rdf/types.jsonld +115 -0
- package/package.json +2 -2
- package/config/runtime-open.json +0 -22
- package/dist/authorization/AuthModeSelector.d.ts +0 -10
- package/dist/authorization/AuthModeSelector.js +0 -27
- package/dist/authorization/AuthModeSelector.js.map +0 -1
- package/dist/authorization/AuthModeSelector.jsonld +0 -81
|
@@ -7,363 +7,206 @@ exports.SqliteQuintStore = void 0;
|
|
|
7
7
|
const node_crypto_1 = require("node:crypto");
|
|
8
8
|
const node_fs_1 = require("node:fs");
|
|
9
9
|
const node_path_1 = require("node:path");
|
|
10
|
-
const
|
|
11
|
-
const asynciterator_1 = require("asynciterator");
|
|
12
|
-
const n3_1 = require("n3");
|
|
13
|
-
const schema_1 = require("./schema");
|
|
10
|
+
const BaseQuintStore_1 = require("./BaseQuintStore");
|
|
14
11
|
const serialization_1 = require("./serialization");
|
|
15
12
|
const SqliteRuntime_1 = require("../SqliteRuntime");
|
|
16
|
-
const types_1 = require("./types");
|
|
17
13
|
const value_types_1 = require("./value-types");
|
|
18
14
|
const SQLITE_UNBOUNDED_LIMIT = Number.MAX_SAFE_INTEGER;
|
|
19
15
|
function digestObject(value) {
|
|
20
16
|
return (0, node_crypto_1.createHash)('sha256').update(value).digest('hex');
|
|
21
17
|
}
|
|
22
|
-
class
|
|
18
|
+
class SqliteExecutor {
|
|
19
|
+
constructor(db) {
|
|
20
|
+
this.db = db;
|
|
21
|
+
}
|
|
22
|
+
async query(sqlText, params) {
|
|
23
|
+
return this.db.prepare(sqlText).all(...(params ?? []));
|
|
24
|
+
}
|
|
25
|
+
async execute(sqlText, params) {
|
|
26
|
+
return this.db.prepare(sqlText).run(...(params ?? [])).changes;
|
|
27
|
+
}
|
|
28
|
+
async executeInTransaction(statements) {
|
|
29
|
+
this.db.transaction(() => {
|
|
30
|
+
for (const statement of statements) {
|
|
31
|
+
this.db.prepare(statement.sql).run(...(statement.params ?? []));
|
|
32
|
+
}
|
|
33
|
+
})();
|
|
34
|
+
}
|
|
35
|
+
async exec(sqlText) {
|
|
36
|
+
this.db.exec(sqlText);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
class SqliteQuintStore extends BaseQuintStore_1.BaseQuintStore {
|
|
23
40
|
constructor(options) {
|
|
41
|
+
const path = options.path.startsWith('sqlite:') ? options.path.slice(7) : options.path;
|
|
42
|
+
super(options);
|
|
24
43
|
this.sqlite = null;
|
|
25
|
-
this.db = null;
|
|
26
44
|
this.sqliteRuntime = (0, SqliteRuntime_1.getSqliteRuntime)();
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
this.options = { ...options, path };
|
|
33
|
-
}
|
|
34
|
-
// ============================================
|
|
35
|
-
// Lifecycle
|
|
36
|
-
// ============================================
|
|
37
|
-
async open() {
|
|
38
|
-
// Idempotent: if already open, do nothing
|
|
39
|
-
if (this.sqlite) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
const dbPath = this.options.path;
|
|
43
|
-
// Ensure directory exists (unless it's in-memory)
|
|
45
|
+
this.path = path;
|
|
46
|
+
}
|
|
47
|
+
async createExecutor() {
|
|
48
|
+
const dbPath = this.path;
|
|
44
49
|
if (dbPath !== ':memory:') {
|
|
45
50
|
const dir = (0, node_path_1.dirname)(dbPath);
|
|
46
|
-
if (!(0, node_fs_1.existsSync)(dir)) {
|
|
51
|
+
if (dir && !(0, node_fs_1.existsSync)(dir)) {
|
|
47
52
|
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
48
53
|
}
|
|
49
54
|
}
|
|
50
55
|
this.sqlite = this.sqliteRuntime.openDatabase(dbPath);
|
|
51
|
-
|
|
52
|
-
// Create table and indexes
|
|
53
|
-
this.sqlite.exec(`
|
|
54
|
-
CREATE TABLE IF NOT EXISTS quints (
|
|
55
|
-
object_kind TEXT,
|
|
56
|
-
object_key TEXT,
|
|
57
|
-
object_text TEXT,
|
|
58
|
-
object_digest TEXT,
|
|
59
|
-
graph TEXT NOT NULL,
|
|
60
|
-
subject TEXT NOT NULL,
|
|
61
|
-
predicate TEXT NOT NULL,
|
|
62
|
-
object TEXT NOT NULL,
|
|
63
|
-
vector TEXT
|
|
64
|
-
);
|
|
65
|
-
`);
|
|
66
|
-
this.ensureTypedObjectSchema();
|
|
67
|
-
this.sqlite.exec(`
|
|
68
|
-
CREATE INDEX IF NOT EXISTS idx_quints_graph ON quints (graph);
|
|
69
|
-
CREATE INDEX IF NOT EXISTS idx_quints_subject ON quints (subject);
|
|
70
|
-
CREATE INDEX IF NOT EXISTS idx_quints_predicate ON quints (predicate);
|
|
71
|
-
CREATE INDEX IF NOT EXISTS idx_quints_object_key ON quints (object_kind, object_key);
|
|
72
|
-
CREATE INDEX IF NOT EXISTS idx_quints_predicate_object_key ON quints (predicate, object_kind, object_key);
|
|
73
|
-
CREATE INDEX IF NOT EXISTS idx_quints_predicate_object_digest ON quints (predicate, object_kind, object_digest);
|
|
74
|
-
CREATE UNIQUE INDEX IF NOT EXISTS idx_quints_gspo_key
|
|
75
|
-
ON quints (graph, subject, predicate, object_kind, object_key)
|
|
76
|
-
WHERE object_key IS NOT NULL;
|
|
77
|
-
CREATE UNIQUE INDEX IF NOT EXISTS idx_quints_gspo_digest
|
|
78
|
-
ON quints (graph, subject, predicate, object_kind, object_digest)
|
|
79
|
-
WHERE object_digest IS NOT NULL;
|
|
80
|
-
CREATE INDEX IF NOT EXISTS idx_quints_gsp ON quints (graph, subject, predicate);
|
|
81
|
-
CREATE INDEX IF NOT EXISTS idx_quints_sp ON quints (subject, predicate);
|
|
82
|
-
CREATE INDEX IF NOT EXISTS idx_quints_gp ON quints (graph, predicate);
|
|
83
|
-
`);
|
|
56
|
+
return new SqliteExecutor(this.sqlite);
|
|
84
57
|
}
|
|
85
|
-
async
|
|
58
|
+
async closeExecutor() {
|
|
86
59
|
if (this.sqlite) {
|
|
87
60
|
this.sqlite.close();
|
|
88
61
|
this.sqlite = null;
|
|
89
|
-
this.db = null;
|
|
90
62
|
}
|
|
91
63
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
this.ensureOpen();
|
|
118
|
-
const { whereClause, params } = this.buildWhereClause(pattern);
|
|
119
|
-
const result = this.sqlite.prepare(`SELECT COUNT(*) AS count FROM quints${whereClause}`).get(...params);
|
|
120
|
-
return Number(result?.count ?? 0);
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Compound query - multiple patterns JOINed by a common field
|
|
124
|
-
* This executes a single SQL query with JOINs, letting SQLite optimize the execution plan
|
|
125
|
-
*/
|
|
126
|
-
async getCompound(compound, options) {
|
|
127
|
-
this.ensureOpen();
|
|
128
|
-
const { patterns, joinOn, select } = compound;
|
|
129
|
-
if (patterns.length === 0) {
|
|
130
|
-
return [];
|
|
131
|
-
}
|
|
132
|
-
if (patterns.length === 1) {
|
|
133
|
-
// Single pattern, fall back to regular get
|
|
134
|
-
const quads = await this.get(patterns[0], options);
|
|
135
|
-
return quads.map(q => ({
|
|
136
|
-
joinValue: (0, serialization_1.termToId)(q[joinOn]),
|
|
137
|
-
bindings: {},
|
|
138
|
-
quads: [q],
|
|
139
|
-
}));
|
|
140
|
-
}
|
|
141
|
-
// Build JOIN SQL
|
|
142
|
-
const { sql: sqlQuery, params } = this.buildCompoundSQL(compound, options);
|
|
143
|
-
if (this.options.debug) {
|
|
144
|
-
console.log('[SqliteQuintStore] Compound SQL:', sqlQuery);
|
|
145
|
-
console.log('[SqliteQuintStore] Params:', params);
|
|
146
|
-
}
|
|
147
|
-
// Execute raw SQL
|
|
148
|
-
const stmt = this.sqlite.prepare(sqlQuery);
|
|
149
|
-
const rows = stmt.all(...params);
|
|
150
|
-
// Convert rows to CompoundResult
|
|
151
|
-
return rows.map(row => {
|
|
152
|
-
const bindings = {};
|
|
153
|
-
// Extract bindings based on select config or default naming
|
|
154
|
-
if (select) {
|
|
155
|
-
for (const s of select) {
|
|
156
|
-
bindings[s.alias] = row[s.alias];
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
// Default: include all fields from all patterns
|
|
161
|
-
for (const key of Object.keys(row)) {
|
|
162
|
-
if (key !== 'join_value') {
|
|
163
|
-
bindings[key] = row[key];
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
return {
|
|
168
|
-
joinValue: row.join_value,
|
|
169
|
-
bindings,
|
|
170
|
-
};
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* 批量获取多个 subject 的多个属性
|
|
175
|
-
*
|
|
176
|
-
* 用于优化 OPTIONAL 查询:避免每个 OPTIONAL 变成一次 LEFT JOIN
|
|
177
|
-
*
|
|
178
|
-
* SQL: SELECT subject, predicate, object FROM quints
|
|
179
|
-
* WHERE subject IN (...) AND predicate IN (...)
|
|
180
|
-
*/
|
|
181
|
-
async getAttributes(subjects, predicates, graph) {
|
|
182
|
-
this.ensureOpen();
|
|
183
|
-
if (subjects.length === 0 || predicates.length === 0) {
|
|
184
|
-
return new Map();
|
|
185
|
-
}
|
|
186
|
-
// Build SQL with IN clauses
|
|
187
|
-
const params = [];
|
|
188
|
-
let sql = `SELECT subject, predicate, object FROM quints WHERE subject IN (${subjects.map(() => '?').join(', ')}) AND predicate IN (${predicates.map(() => '?').join(', ')})`;
|
|
189
|
-
params.push(...subjects);
|
|
190
|
-
params.push(...predicates);
|
|
191
|
-
// Add graph filter if specified
|
|
192
|
-
if (graph && graph.termType !== 'DefaultGraph') {
|
|
193
|
-
sql += ` AND graph = ?`;
|
|
194
|
-
params.push((0, serialization_1.termToId)(graph));
|
|
195
|
-
}
|
|
196
|
-
if (this.options.debug) {
|
|
197
|
-
console.log('[SqliteQuintStore] getAttributes SQL:', sql);
|
|
198
|
-
console.log('[SqliteQuintStore] Params:', params.length, 'subjects:', subjects.length, 'predicates:', predicates.length);
|
|
199
|
-
}
|
|
200
|
-
const stmt = this.sqlite.prepare(sql);
|
|
201
|
-
const rows = stmt.all(...params);
|
|
202
|
-
// Build result map: subject -> predicate -> object[]
|
|
203
|
-
const result = new Map();
|
|
204
|
-
for (const row of rows) {
|
|
205
|
-
if (!result.has(row.subject)) {
|
|
206
|
-
result.set(row.subject, new Map());
|
|
207
|
-
}
|
|
208
|
-
const predicateMap = result.get(row.subject);
|
|
209
|
-
if (!predicateMap.has(row.predicate)) {
|
|
210
|
-
predicateMap.set(row.predicate, []);
|
|
64
|
+
async openOnce() {
|
|
65
|
+
const executor = await this.createExecutor();
|
|
66
|
+
this.executor = executor;
|
|
67
|
+
try {
|
|
68
|
+
await executor.exec(`
|
|
69
|
+
CREATE TABLE IF NOT EXISTS quints (
|
|
70
|
+
object_kind TEXT,
|
|
71
|
+
object_key TEXT,
|
|
72
|
+
object_text TEXT,
|
|
73
|
+
object_digest TEXT,
|
|
74
|
+
graph TEXT NOT NULL,
|
|
75
|
+
subject TEXT NOT NULL,
|
|
76
|
+
predicate TEXT NOT NULL,
|
|
77
|
+
object TEXT NOT NULL,
|
|
78
|
+
vector TEXT
|
|
79
|
+
);
|
|
80
|
+
`);
|
|
81
|
+
await this.ensureTypedObjectSchema();
|
|
82
|
+
await this.createTypedObjectIndexes();
|
|
83
|
+
this.opened = true;
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
await this.closeExecutor().catch(() => { });
|
|
87
|
+
if (this.executor === executor) {
|
|
88
|
+
this.executor = null;
|
|
211
89
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
predicateMap.get(row.predicate).push(objectTerm);
|
|
215
|
-
}
|
|
216
|
-
if (this.options.debug) {
|
|
217
|
-
console.log('[SqliteQuintStore] getAttributes returned', result.size, 'subjects');
|
|
90
|
+
this.opened = false;
|
|
91
|
+
throw error;
|
|
218
92
|
}
|
|
219
|
-
return result;
|
|
220
93
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
for (let i = 0; i < patterns.length; i++) {
|
|
239
|
-
selectClause += `, q${i}.object as p${i}_object`;
|
|
240
|
-
selectClause += `, q${i}.predicate as p${i}_predicate`;
|
|
94
|
+
buildSelectQuery(pattern, options) {
|
|
95
|
+
const { whereClause, params } = this.buildWhereClause(pattern);
|
|
96
|
+
let sqlText = `SELECT * FROM quints${whereClause}`;
|
|
97
|
+
if (options?.order && options.order.length > 0) {
|
|
98
|
+
const orderCols = options.order.map(field => {
|
|
99
|
+
if (field === 'object') {
|
|
100
|
+
const objectType = this.resolveObjectDataTypeForPattern(pattern);
|
|
101
|
+
if (objectType === 'longText') {
|
|
102
|
+
throw new Error('ORDER BY object is not supported for longText predicates');
|
|
103
|
+
}
|
|
104
|
+
return 'object_key';
|
|
105
|
+
}
|
|
106
|
+
return field;
|
|
107
|
+
}).join(', ');
|
|
108
|
+
sqlText += ` ORDER BY ${orderCols}`;
|
|
109
|
+
if (options.reverse) {
|
|
110
|
+
sqlText += ' DESC';
|
|
241
111
|
}
|
|
242
112
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
for (let i = 1; i < patterns.length; i++) {
|
|
246
|
-
fromClause += ` JOIN quints q${i} ON q0.${joinColumn} = q${i}.${joinColumn}`;
|
|
247
|
-
// Also join on graph to ensure same graph
|
|
248
|
-
fromClause += ` AND q0.graph = q${i}.graph`;
|
|
249
|
-
}
|
|
250
|
-
// Build WHERE clause
|
|
251
|
-
const whereParts = [];
|
|
252
|
-
for (let i = 0; i < patterns.length; i++) {
|
|
253
|
-
const pattern = patterns[i];
|
|
254
|
-
const alias = `q${i}`;
|
|
255
|
-
const conditions = this.buildConditionsForAlias(pattern, alias, params);
|
|
256
|
-
whereParts.push(...conditions);
|
|
257
|
-
}
|
|
258
|
-
let sql = `SELECT ${selectClause} FROM ${fromClause}`;
|
|
259
|
-
if (whereParts.length > 0) {
|
|
260
|
-
sql += ` WHERE ${whereParts.join(' AND ')}`;
|
|
261
|
-
}
|
|
262
|
-
// Add LIMIT/OFFSET
|
|
263
|
-
if (options?.limit) {
|
|
264
|
-
sql += ` LIMIT ?`;
|
|
113
|
+
if (options?.limit !== undefined) {
|
|
114
|
+
sqlText += ` LIMIT ?`;
|
|
265
115
|
params.push(options.limit);
|
|
266
116
|
}
|
|
267
|
-
if (options?.offset) {
|
|
268
|
-
if (
|
|
269
|
-
|
|
117
|
+
if (options?.offset !== undefined) {
|
|
118
|
+
if (options.limit === undefined) {
|
|
119
|
+
sqlText += ` LIMIT ?`;
|
|
120
|
+
params.push(SQLITE_UNBOUNDED_LIMIT);
|
|
270
121
|
}
|
|
271
|
-
|
|
122
|
+
sqlText += ` OFFSET ?`;
|
|
272
123
|
params.push(options.offset);
|
|
273
124
|
}
|
|
274
|
-
return { sql, params };
|
|
125
|
+
return { sql: sqlText, params };
|
|
275
126
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
if (pattern.object) {
|
|
285
|
-
this.addObjectConditions(conditions, params, alias, pattern.object, this.extractExactPredicate(pattern.predicate));
|
|
286
|
-
}
|
|
287
|
-
return conditions;
|
|
127
|
+
async stats() {
|
|
128
|
+
const stats = await super.stats();
|
|
129
|
+
return {
|
|
130
|
+
totalCount: Number(stats.totalCount),
|
|
131
|
+
vectorCount: Number(stats.vectorCount),
|
|
132
|
+
graphCount: Number(stats.graphCount),
|
|
133
|
+
...this.sqliteSpaceStats(),
|
|
134
|
+
};
|
|
288
135
|
}
|
|
289
|
-
|
|
290
|
-
// Write Operations
|
|
291
|
-
// ============================================
|
|
292
|
-
async put(quint) {
|
|
136
|
+
async clear() {
|
|
293
137
|
this.ensureOpen();
|
|
294
|
-
|
|
295
|
-
const statement = this.writeStatementForRow(row);
|
|
296
|
-
this.sqlite.prepare(statement.sql).run(...statement.params);
|
|
138
|
+
await this.executor.execute('DELETE FROM quints');
|
|
297
139
|
}
|
|
298
|
-
async
|
|
299
|
-
this.
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
140
|
+
async ensureTypedObjectSchema() {
|
|
141
|
+
await this.addColumnIfMissing('object_kind', 'TEXT');
|
|
142
|
+
await this.addColumnIfMissing('object_key', 'TEXT');
|
|
143
|
+
await this.addColumnIfMissing('object_text', 'TEXT');
|
|
144
|
+
await this.addColumnIfMissing('object_digest', 'TEXT');
|
|
145
|
+
for (const indexName of ['idx_spog', 'idx_ogsp', 'idx_gspo', 'idx_sopg', 'idx_pogs', 'idx_gpos']) {
|
|
146
|
+
await this.executor.exec(`DROP INDEX IF EXISTS ${indexName}`);
|
|
147
|
+
}
|
|
148
|
+
await this.backfillMissingObjectIndexFields();
|
|
149
|
+
await this.executor.exec(`
|
|
150
|
+
UPDATE quints
|
|
151
|
+
SET object_kind = 'text'
|
|
152
|
+
WHERE object_kind = 'shortText'
|
|
153
|
+
`);
|
|
309
154
|
}
|
|
310
|
-
async
|
|
311
|
-
this.
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
return result.changes;
|
|
316
|
-
}
|
|
317
|
-
// ============================================
|
|
318
|
-
// Delete Operations
|
|
319
|
-
// ============================================
|
|
320
|
-
async del(pattern) {
|
|
321
|
-
this.ensureOpen();
|
|
322
|
-
const { whereClause, params } = this.buildWhereClause(pattern);
|
|
323
|
-
const result = this.sqlite.prepare(`DELETE FROM quints${whereClause}`).run(...params);
|
|
324
|
-
return result.changes;
|
|
155
|
+
async addColumnIfMissing(name, definition) {
|
|
156
|
+
const columns = new Set(this.sqlite.prepare('PRAGMA table_info(quints)').all().map(row => row.name));
|
|
157
|
+
if (!columns.has(name)) {
|
|
158
|
+
await this.executor.exec(`ALTER TABLE quints ADD COLUMN ${name} ${definition}`);
|
|
159
|
+
}
|
|
325
160
|
}
|
|
326
|
-
async
|
|
327
|
-
this.
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
})();
|
|
161
|
+
async createTypedObjectIndexes() {
|
|
162
|
+
await this.executor.exec(`
|
|
163
|
+
CREATE INDEX IF NOT EXISTS idx_quints_graph ON quints (graph);
|
|
164
|
+
CREATE INDEX IF NOT EXISTS idx_quints_subject ON quints (subject);
|
|
165
|
+
CREATE INDEX IF NOT EXISTS idx_quints_predicate ON quints (predicate);
|
|
166
|
+
CREATE INDEX IF NOT EXISTS idx_quints_object_key ON quints (object_kind, object_key);
|
|
167
|
+
CREATE INDEX IF NOT EXISTS idx_quints_predicate_object_key ON quints (predicate, object_kind, object_key);
|
|
168
|
+
CREATE INDEX IF NOT EXISTS idx_quints_predicate_object_digest ON quints (predicate, object_kind, object_digest);
|
|
169
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_quints_gspo_key
|
|
170
|
+
ON quints (graph, subject, predicate, object_kind, object_key)
|
|
171
|
+
WHERE object_key IS NOT NULL;
|
|
172
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_quints_gspo_digest
|
|
173
|
+
ON quints (graph, subject, predicate, object_kind, object_digest)
|
|
174
|
+
WHERE object_digest IS NOT NULL;
|
|
175
|
+
CREATE INDEX IF NOT EXISTS idx_quints_gsp ON quints (graph, subject, predicate);
|
|
176
|
+
CREATE INDEX IF NOT EXISTS idx_quints_sp ON quints (subject, predicate);
|
|
177
|
+
CREATE INDEX IF NOT EXISTS idx_quints_gp ON quints (graph, predicate);
|
|
178
|
+
`);
|
|
345
179
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
180
|
+
async backfillMissingObjectIndexFields() {
|
|
181
|
+
const rows = await this.executor.query(`
|
|
182
|
+
SELECT graph, subject, predicate, object
|
|
183
|
+
FROM quints
|
|
184
|
+
WHERE object_kind IS NULL
|
|
185
|
+
OR (object_key IS NULL AND object_digest IS NULL)
|
|
186
|
+
`);
|
|
187
|
+
for (const row of rows) {
|
|
188
|
+
const objectIndex = this.objectIndexForSerialized(row.predicate, row.object);
|
|
189
|
+
await this.executor.execute(`
|
|
190
|
+
UPDATE quints
|
|
191
|
+
SET object_kind = ?,
|
|
192
|
+
object_key = ?,
|
|
193
|
+
object_text = ?,
|
|
194
|
+
object_digest = ?
|
|
195
|
+
WHERE graph = ?
|
|
196
|
+
AND subject = ?
|
|
197
|
+
AND predicate = ?
|
|
198
|
+
AND object = ?
|
|
199
|
+
`, [
|
|
200
|
+
objectIndex.objectKind,
|
|
201
|
+
objectIndex.objectKey,
|
|
202
|
+
objectIndex.objectText,
|
|
203
|
+
this.objectDigestForIndex(row.object, objectIndex),
|
|
204
|
+
row.graph,
|
|
205
|
+
row.subject,
|
|
206
|
+
row.predicate,
|
|
207
|
+
row.object,
|
|
208
|
+
]);
|
|
209
|
+
}
|
|
367
210
|
}
|
|
368
211
|
sqliteSpaceStats() {
|
|
369
212
|
const spaceObjects = this.collectSpaceObjects();
|
|
@@ -447,94 +290,25 @@ class SqliteQuintStore {
|
|
|
447
290
|
return 4096;
|
|
448
291
|
}
|
|
449
292
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
// ============================================
|
|
457
|
-
ensureOpen() {
|
|
458
|
-
if (!this.db) {
|
|
459
|
-
throw new Error('Store not open. Call open() first.');
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
ensureTypedObjectSchema() {
|
|
463
|
-
this.addColumnIfMissing('object_kind', 'TEXT');
|
|
464
|
-
this.addColumnIfMissing('object_key', 'TEXT');
|
|
465
|
-
this.addColumnIfMissing('object_text', 'TEXT');
|
|
466
|
-
this.addColumnIfMissing('object_digest', 'TEXT');
|
|
467
|
-
for (const indexName of ['idx_spog', 'idx_ogsp', 'idx_gspo', 'idx_sopg', 'idx_pogs', 'idx_gpos']) {
|
|
468
|
-
this.sqlite.exec(`DROP INDEX IF EXISTS ${indexName}`);
|
|
469
|
-
}
|
|
470
|
-
this.backfillMissingObjectIndexFields();
|
|
471
|
-
this.sqlite.prepare(`
|
|
472
|
-
UPDATE quints
|
|
473
|
-
SET object_kind = 'text'
|
|
474
|
-
WHERE object_kind = 'shortText'
|
|
475
|
-
`).run();
|
|
476
|
-
}
|
|
477
|
-
addColumnIfMissing(name, definition) {
|
|
478
|
-
const columns = new Set(this.sqlite.prepare('PRAGMA table_info(quints)').all().map(row => row.name));
|
|
479
|
-
if (!columns.has(name)) {
|
|
480
|
-
this.sqlite.exec(`ALTER TABLE quints ADD COLUMN ${name} ${definition}`);
|
|
481
|
-
}
|
|
293
|
+
objectIndexForSerialized(predicate, object) {
|
|
294
|
+
return (0, value_types_1.objectIndexFieldsFromSerialized)(object, {
|
|
295
|
+
predicate,
|
|
296
|
+
predicateObjectDataTypes: this.options.predicateObjectDataTypes,
|
|
297
|
+
textMaxBytes: this.options.textMaxBytes,
|
|
298
|
+
});
|
|
482
299
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
SELECT graph, subject, predicate, object
|
|
486
|
-
FROM quints
|
|
487
|
-
WHERE object_kind IS NULL
|
|
488
|
-
OR (object_key IS NULL AND object_digest IS NULL)
|
|
489
|
-
`).all();
|
|
490
|
-
const update = this.sqlite.prepare(`
|
|
491
|
-
UPDATE quints
|
|
492
|
-
SET object_kind = ?,
|
|
493
|
-
object_key = ?,
|
|
494
|
-
object_text = ?,
|
|
495
|
-
object_digest = ?
|
|
496
|
-
WHERE graph = ?
|
|
497
|
-
AND subject = ?
|
|
498
|
-
AND predicate = ?
|
|
499
|
-
AND object = ?
|
|
500
|
-
`);
|
|
501
|
-
for (const row of rows) {
|
|
502
|
-
const objectIndex = this.objectIndexForSerialized(row.predicate, row.object);
|
|
503
|
-
update.run(objectIndex.objectKind, objectIndex.objectKey, objectIndex.objectText, this.objectDigestForIndex(row.object, objectIndex), row.graph, row.subject, row.predicate, row.object);
|
|
504
|
-
}
|
|
300
|
+
objectDigestForIndex(serialized, fields) {
|
|
301
|
+
return fields.objectKey === null ? digestObject(serialized) : null;
|
|
505
302
|
}
|
|
506
|
-
|
|
507
|
-
const
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
const orderCols = options.order.map(field => {
|
|
511
|
-
if (field === 'object') {
|
|
512
|
-
const objectType = this.resolveObjectDataTypeForPattern(pattern);
|
|
513
|
-
if (objectType === 'longText') {
|
|
514
|
-
throw new Error('ORDER BY object is not supported for longText predicates');
|
|
515
|
-
}
|
|
516
|
-
return 'object_key';
|
|
517
|
-
}
|
|
518
|
-
return field;
|
|
519
|
-
}).join(', ');
|
|
520
|
-
query += ` ORDER BY ${orderCols}`;
|
|
521
|
-
if (options.reverse) {
|
|
522
|
-
query += ' DESC';
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
if (options?.limit !== undefined) {
|
|
526
|
-
query += ' LIMIT ?';
|
|
527
|
-
params.push(options.limit);
|
|
303
|
+
resolveObjectDataTypeForPattern(pattern) {
|
|
304
|
+
const predicate = this.extractExactPredicate(pattern.predicate);
|
|
305
|
+
if (predicate) {
|
|
306
|
+
return (0, value_types_1.getPredicateObjectDataType)(predicate, this.options.predicateObjectDataTypes);
|
|
528
307
|
}
|
|
529
|
-
if (
|
|
530
|
-
|
|
531
|
-
query += ' LIMIT ?';
|
|
532
|
-
params.push(SQLITE_UNBOUNDED_LIMIT);
|
|
533
|
-
}
|
|
534
|
-
query += ' OFFSET ?';
|
|
535
|
-
params.push(options.offset);
|
|
308
|
+
if (pattern.object && typeof pattern.object === 'object' && 'termType' in pattern.object) {
|
|
309
|
+
return this.objectIndexForTerm(predicate, pattern.object).objectKind;
|
|
536
310
|
}
|
|
537
|
-
return
|
|
311
|
+
return undefined;
|
|
538
312
|
}
|
|
539
313
|
buildWhereClause(pattern) {
|
|
540
314
|
const conditions = [];
|
|
@@ -552,93 +326,15 @@ class SqliteQuintStore {
|
|
|
552
326
|
};
|
|
553
327
|
}
|
|
554
328
|
addTermConditions(conditions, params, column, match, isObject) {
|
|
555
|
-
|
|
556
|
-
return;
|
|
557
|
-
if ((0, types_1.isTerm)(match)) {
|
|
558
|
-
conditions.push(`${column} = ?`);
|
|
559
|
-
params.push(isObject ? (0, serialization_1.serializeObject)(match) : (0, serialization_1.termToId)(match));
|
|
560
|
-
return;
|
|
561
|
-
}
|
|
562
|
-
const ops = match;
|
|
563
|
-
if (ops.$eq !== undefined) {
|
|
564
|
-
conditions.push(`${column} = ?`);
|
|
565
|
-
params.push(this.serializeOpValue(ops.$eq, isObject, '$eq'));
|
|
566
|
-
}
|
|
567
|
-
if (ops.$ne !== undefined) {
|
|
568
|
-
conditions.push(`${column} != ?`);
|
|
569
|
-
params.push(this.serializeOpValue(ops.$ne, isObject, '$ne'));
|
|
570
|
-
}
|
|
571
|
-
if (ops.$gt !== undefined) {
|
|
572
|
-
conditions.push(`${column} > ?`);
|
|
573
|
-
params.push(this.serializeOpValue(ops.$gt, isObject, '$gt'));
|
|
574
|
-
}
|
|
575
|
-
if (ops.$gte !== undefined) {
|
|
576
|
-
conditions.push(`${column} >= ?`);
|
|
577
|
-
params.push(this.serializeOpValue(ops.$gte, isObject, '$gte'));
|
|
578
|
-
}
|
|
579
|
-
if (ops.$lt !== undefined) {
|
|
580
|
-
conditions.push(`${column} < ?`);
|
|
581
|
-
params.push(this.serializeOpValue(ops.$lt, isObject, '$lt'));
|
|
582
|
-
}
|
|
583
|
-
if (ops.$lte !== undefined) {
|
|
584
|
-
conditions.push(`${column} <= ?`);
|
|
585
|
-
params.push(this.serializeOpValue(ops.$lte, isObject, '$lte'));
|
|
586
|
-
}
|
|
587
|
-
if (ops.$in !== undefined && ops.$in.length > 0) {
|
|
588
|
-
const placeholders = ops.$in.map(() => '?').join(', ');
|
|
589
|
-
conditions.push(`${column} IN (${placeholders})`);
|
|
590
|
-
params.push(...ops.$in.map(v => this.serializeOpValue(v, isObject, '$in')));
|
|
591
|
-
}
|
|
592
|
-
if (ops.$notIn !== undefined && ops.$notIn.length > 0) {
|
|
593
|
-
const placeholders = ops.$notIn.map(() => '?').join(', ');
|
|
594
|
-
conditions.push(`${column} NOT IN (${placeholders})`);
|
|
595
|
-
params.push(...ops.$notIn.map(v => this.serializeOpValue(v, isObject, '$notIn')));
|
|
596
|
-
}
|
|
597
|
-
if (ops.$startsWith !== undefined) {
|
|
598
|
-
conditions.push(`${column} >= ? AND ${column} < ?`);
|
|
599
|
-
params.push(ops.$startsWith, ops.$startsWith + '\uffff');
|
|
600
|
-
}
|
|
601
|
-
if (ops.$endsWith !== undefined) {
|
|
602
|
-
conditions.push(`${column} LIKE ?`);
|
|
603
|
-
params.push(`%${ops.$endsWith}`);
|
|
604
|
-
}
|
|
605
|
-
if (ops.$contains !== undefined) {
|
|
606
|
-
conditions.push(`${column} LIKE ?`);
|
|
607
|
-
params.push(`%${ops.$contains}%`);
|
|
608
|
-
}
|
|
609
|
-
if (ops.$regex !== undefined) {
|
|
610
|
-
conditions.push(`${column} GLOB ?`);
|
|
611
|
-
params.push(ops.$regex.replace(/\.\*/g, '*').replace(/\./g, '?'));
|
|
612
|
-
}
|
|
613
|
-
if (ops.$isNull === true) {
|
|
614
|
-
conditions.push(`${column} IS NULL`);
|
|
615
|
-
}
|
|
616
|
-
if (ops.$isNull === false) {
|
|
617
|
-
conditions.push(`${column} IS NOT NULL`);
|
|
618
|
-
}
|
|
329
|
+
this.addConditions(conditions, params, column, match, isObject);
|
|
619
330
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
}
|
|
624
|
-
if (
|
|
625
|
-
|
|
626
|
-
if (filterOp === '$eq' || filterOp === '$ne' || filterOp === '$in' || filterOp === '$notIn') {
|
|
627
|
-
const lit = n3_1.DataFactory.literal(String(value), n3_1.DataFactory.namedNode('http://www.w3.org/2001/XMLSchema#integer'));
|
|
628
|
-
return (0, serialization_1.serializeObject)(lit);
|
|
629
|
-
}
|
|
630
|
-
const fpValue = `N${serialization_1.SEP}${(0, serialization_1.fpEncode)(value)}`;
|
|
631
|
-
if (filterOp === '$gt' || filterOp === '$lte') {
|
|
632
|
-
return fpValue + serialization_1.SEP + '\uffff';
|
|
633
|
-
}
|
|
634
|
-
return fpValue;
|
|
635
|
-
}
|
|
636
|
-
return value;
|
|
637
|
-
}
|
|
638
|
-
if (isObject && !(0, serialization_1.isSerializedObjectValue)(value)) {
|
|
639
|
-
return `"${value}"`;
|
|
331
|
+
addAliasedConditions(conditions, params, alias, pattern) {
|
|
332
|
+
this.addTermConditions(conditions, params, `${alias}.graph`, pattern.graph, false);
|
|
333
|
+
this.addTermConditions(conditions, params, `${alias}.subject`, pattern.subject, false);
|
|
334
|
+
this.addTermConditions(conditions, params, `${alias}.predicate`, pattern.predicate, false);
|
|
335
|
+
if (pattern.object) {
|
|
336
|
+
this.addObjectConditions(conditions, params, alias, pattern.object, this.extractExactPredicate(pattern.predicate));
|
|
640
337
|
}
|
|
641
|
-
return value;
|
|
642
338
|
}
|
|
643
339
|
addObjectConditions(conditions, params, alias, match, predicate) {
|
|
644
340
|
const column = (name) => alias ? `${alias}.${name}` : name;
|
|
@@ -843,7 +539,7 @@ class SqliteQuintStore {
|
|
|
843
539
|
return { objectKind: 'dateTime', objectKey: serialized, objectText: null };
|
|
844
540
|
}
|
|
845
541
|
}
|
|
846
|
-
return this.objectIndexForSerialized(predicate, serialized);
|
|
542
|
+
return this.objectIndexForSerialized(predicate ?? '', serialized);
|
|
847
543
|
}
|
|
848
544
|
objectIndexForTerm(predicate, object) {
|
|
849
545
|
return (0, value_types_1.objectIndexFieldsFromTerm)(object, {
|
|
@@ -852,13 +548,6 @@ class SqliteQuintStore {
|
|
|
852
548
|
textMaxBytes: this.options.textMaxBytes,
|
|
853
549
|
});
|
|
854
550
|
}
|
|
855
|
-
objectIndexForSerialized(predicate, object) {
|
|
856
|
-
return (0, value_types_1.objectIndexFieldsFromSerialized)(object, {
|
|
857
|
-
predicate,
|
|
858
|
-
predicateObjectDataTypes: this.options.predicateObjectDataTypes,
|
|
859
|
-
textMaxBytes: this.options.textMaxBytes,
|
|
860
|
-
});
|
|
861
|
-
}
|
|
862
551
|
objectFieldsForPrefix(prefix, predicate) {
|
|
863
552
|
const declaredType = (0, value_types_1.getPredicateObjectDataType)(predicate, this.options.predicateObjectDataTypes);
|
|
864
553
|
if (declaredType) {
|
|
@@ -881,40 +570,28 @@ class SqliteQuintStore {
|
|
|
881
570
|
}
|
|
882
571
|
return { objectKind: 'iri', objectKey: prefix, objectText: null };
|
|
883
572
|
}
|
|
884
|
-
objectDigestForIndex(serialized, fields) {
|
|
885
|
-
return fields.objectKey === null ? digestObject(serialized) : null;
|
|
886
|
-
}
|
|
887
573
|
assertComparableObject(fields, op) {
|
|
888
574
|
if (fields.objectKey !== null && fields.objectKind !== 'longText') {
|
|
889
575
|
return;
|
|
890
576
|
}
|
|
891
577
|
throw new Error(`Object ${op} is not supported for ${fields.objectKind}; declare/use a comparable data type instead of longText`);
|
|
892
578
|
}
|
|
893
|
-
extractExactPredicate(match) {
|
|
894
|
-
if (!match)
|
|
895
|
-
return undefined;
|
|
896
|
-
if (typeof match === 'object' && 'termType' in match) {
|
|
897
|
-
return (0, serialization_1.termToId)(match);
|
|
898
|
-
}
|
|
899
|
-
const ops = match;
|
|
900
|
-
if (ops.$eq !== undefined) {
|
|
901
|
-
return String(this.serializeOpValue(ops.$eq, false, '$eq'));
|
|
902
|
-
}
|
|
903
|
-
return undefined;
|
|
904
|
-
}
|
|
905
|
-
resolveObjectDataTypeForPattern(pattern) {
|
|
906
|
-
const predicate = this.extractExactPredicate(pattern.predicate);
|
|
907
|
-
if (predicate) {
|
|
908
|
-
return (0, value_types_1.getPredicateObjectDataType)(predicate, this.options.predicateObjectDataTypes);
|
|
909
|
-
}
|
|
910
|
-
if (pattern.object && typeof pattern.object === 'object' && 'termType' in pattern.object) {
|
|
911
|
-
return this.objectIndexForTerm(predicate, pattern.object).objectKind;
|
|
912
|
-
}
|
|
913
|
-
return undefined;
|
|
914
|
-
}
|
|
915
579
|
predicateForIndex(predicate) {
|
|
916
580
|
return predicate;
|
|
917
581
|
}
|
|
582
|
+
async put(quint) {
|
|
583
|
+
this.ensureOpen();
|
|
584
|
+
const row = this.quintToRow(quint);
|
|
585
|
+
const statement = this.writeStatementForRow(row);
|
|
586
|
+
await this.executor.execute(statement.sql, statement.params);
|
|
587
|
+
}
|
|
588
|
+
async multiPut(quintList) {
|
|
589
|
+
this.ensureOpen();
|
|
590
|
+
if (quintList.length === 0)
|
|
591
|
+
return;
|
|
592
|
+
const statements = quintList.map(quint => this.writeStatementForRow(this.quintToRow(quint)));
|
|
593
|
+
await this.executor.executeInTransaction(statements);
|
|
594
|
+
}
|
|
918
595
|
writeStatementForRow(row) {
|
|
919
596
|
const params = [
|
|
920
597
|
row.objectKind,
|