orez 0.2.34 → 0.3.0
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/bench/serial-mutations.bench.js +1 -1
- package/dist/cf-do/worker.d.ts +1 -1
- package/dist/pg-proxy-do-backend.d.ts +6 -0
- package/dist/pg-proxy-do-backend.d.ts.map +1 -1
- package/dist/pg-proxy-do-backend.js +230 -21
- package/dist/pg-proxy-do-backend.js.map +1 -1
- package/dist/pglite-manager.js +1 -1
- package/package.json +4 -4
|
@@ -10,7 +10,7 @@ import WebSocket from 'ws';
|
|
|
10
10
|
import { startZeroLite } from '../index.js';
|
|
11
11
|
import { ensureTablesInPublications, installAllowAllPermissions, } from '../integration/test-permissions.js';
|
|
12
12
|
import { installChangeTracking } from '../replication/change-tracker.js';
|
|
13
|
-
const SYNC_PROTOCOL_VERSION =
|
|
13
|
+
const SYNC_PROTOCOL_VERSION = 50;
|
|
14
14
|
const NUM_MUTATIONS = 100;
|
|
15
15
|
// test schema
|
|
16
16
|
const CLIENT_SCHEMA = {
|
package/dist/cf-do/worker.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { DurableObject } from 'cloudflare:workers';
|
|
|
9
9
|
* for zero-cache.
|
|
10
10
|
*
|
|
11
11
|
* Modes:
|
|
12
|
-
* WS /sync/
|
|
12
|
+
* WS /sync/v50/connect — bespoke Zero sync protocol (dev/protocol testing)
|
|
13
13
|
* POST /exec — raw SQL execution (from DoBackend adapter)
|
|
14
14
|
* POST /batch — atomic batch execution via ctx.storage.transaction()
|
|
15
15
|
*/
|
|
@@ -57,6 +57,8 @@ export declare class DoBackend {
|
|
|
57
57
|
private trackingForStatement;
|
|
58
58
|
private trackingRequest;
|
|
59
59
|
private visibleResultForTracking;
|
|
60
|
+
private currentPublishedSchema;
|
|
61
|
+
private materializePublishedSchemaFunctions;
|
|
60
62
|
private handleParse;
|
|
61
63
|
private handleBind;
|
|
62
64
|
private handleExecute;
|
|
@@ -109,6 +111,7 @@ export declare class DoBackend {
|
|
|
109
111
|
private publicationTableSpec;
|
|
110
112
|
private publishedTablesResult;
|
|
111
113
|
private publishedIndexesResult;
|
|
114
|
+
private publishedSchemaResult;
|
|
112
115
|
private pgTypeArrayResult;
|
|
113
116
|
private sqlLiteral;
|
|
114
117
|
private inlineParams;
|
|
@@ -116,6 +119,9 @@ export declare class DoBackend {
|
|
|
116
119
|
private sqliteBoundSQL;
|
|
117
120
|
private buildSQLResponse;
|
|
118
121
|
private copyTextValue;
|
|
122
|
+
private copyBinaryValue;
|
|
123
|
+
private copyBinaryRow;
|
|
124
|
+
private buildBinaryCopyResponse;
|
|
119
125
|
private buildCopyResponse;
|
|
120
126
|
private fieldMetadataForResultColumn;
|
|
121
127
|
private fieldsForResult;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pg-proxy-do-backend.d.ts","sourceRoot":"","sources":["../src/pg-proxy-do-backend.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pg-proxy-do-backend.d.ts","sourceRoot":"","sources":["../src/pg-proxy-do-backend.ts"],"names":[],"mappings":"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"}
|
|
@@ -60,6 +60,10 @@ function i32(v, buf = new ArrayBuffer(4)) {
|
|
|
60
60
|
new DataView(buf).setInt32(0, v);
|
|
61
61
|
return new Uint8Array(buf);
|
|
62
62
|
}
|
|
63
|
+
function i64(v, buf = new ArrayBuffer(8)) {
|
|
64
|
+
new DataView(buf).setBigInt64(0, v);
|
|
65
|
+
return new Uint8Array(buf);
|
|
66
|
+
}
|
|
63
67
|
function int4(v, buf = new ArrayBuffer(4)) {
|
|
64
68
|
new DataView(buf).setInt32(0, v);
|
|
65
69
|
return new Uint8Array(buf);
|
|
@@ -946,7 +950,7 @@ function rewriteColumnRefQualifier(node, from, to) {
|
|
|
946
950
|
return;
|
|
947
951
|
}
|
|
948
952
|
const fields = node.ColumnRef?.fields;
|
|
949
|
-
if (Array.isArray(fields) && stringValue(fields[0]) === from) {
|
|
953
|
+
if (Array.isArray(fields) && fields.length > 1 && stringValue(fields[0]) === from) {
|
|
950
954
|
fields[0] = stringNode(to);
|
|
951
955
|
}
|
|
952
956
|
for (const child of Object.values(node)) {
|
|
@@ -1510,12 +1514,15 @@ function unwrapJsonEachArraySubLinkArg(arg) {
|
|
|
1510
1514
|
function isDistinctOnClause(clause) {
|
|
1511
1515
|
return (Array.isArray(clause) && clause.some((item) => item && Object.keys(item).length > 0));
|
|
1512
1516
|
}
|
|
1513
|
-
function buildCopyOutResponse(columnCount) {
|
|
1514
|
-
return msg(0x48, concat(new Uint8Array([0]), i16(columnCount), ...Array.from({ length: columnCount }, () => i16(0))));
|
|
1517
|
+
function buildCopyOutResponse(columnCount, binary = false) {
|
|
1518
|
+
return msg(0x48, concat(new Uint8Array([binary ? 1 : 0]), i16(columnCount), ...Array.from({ length: columnCount }, () => i16(binary ? 1 : 0))));
|
|
1515
1519
|
}
|
|
1516
1520
|
function buildCopyData(data) {
|
|
1517
1521
|
return msg(0x64, textEncoder.encode(data));
|
|
1518
1522
|
}
|
|
1523
|
+
function buildCopyDataBytes(data) {
|
|
1524
|
+
return msg(0x64, data);
|
|
1525
|
+
}
|
|
1519
1526
|
function buildCopyDone() {
|
|
1520
1527
|
return msg(0x63, new Uint8Array(0));
|
|
1521
1528
|
}
|
|
@@ -1640,8 +1647,78 @@ function rewriteDistinctOnSelect(stmt) {
|
|
|
1640
1647
|
op: 'SETOP_NONE',
|
|
1641
1648
|
};
|
|
1642
1649
|
}
|
|
1650
|
+
function flattenSelectRangeVarQualifiers(stmt) {
|
|
1651
|
+
const visitFromNode = (node) => {
|
|
1652
|
+
if (!node || typeof node !== 'object')
|
|
1653
|
+
return;
|
|
1654
|
+
const rangeVar = node.RangeVar;
|
|
1655
|
+
if (rangeVar?.schemaname && rangeVar.relname) {
|
|
1656
|
+
const from = rangeVar.alias?.aliasname ? null : rangeVar.relname;
|
|
1657
|
+
const to = flattenRangeVar(rangeVar);
|
|
1658
|
+
if (from && to)
|
|
1659
|
+
rewriteColumnRefQualifier(stmt, from, to);
|
|
1660
|
+
return;
|
|
1661
|
+
}
|
|
1662
|
+
const join = node.JoinExpr;
|
|
1663
|
+
if (join) {
|
|
1664
|
+
visitFromNode(join.larg);
|
|
1665
|
+
visitFromNode(join.rarg);
|
|
1666
|
+
}
|
|
1667
|
+
};
|
|
1668
|
+
for (const fromNode of stmt?.fromClause ?? [])
|
|
1669
|
+
visitFromNode(fromNode);
|
|
1670
|
+
}
|
|
1671
|
+
function selectRangeVarNames(stmt) {
|
|
1672
|
+
const names = new Set();
|
|
1673
|
+
const visitFromNode = (node) => {
|
|
1674
|
+
if (!node || typeof node !== 'object')
|
|
1675
|
+
return;
|
|
1676
|
+
const rangeVar = node.RangeVar;
|
|
1677
|
+
if (rangeVar?.relname) {
|
|
1678
|
+
names.add(rangeVar.relname);
|
|
1679
|
+
if (rangeVar.alias?.aliasname)
|
|
1680
|
+
names.add(rangeVar.alias.aliasname);
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1683
|
+
const join = node.JoinExpr;
|
|
1684
|
+
if (join) {
|
|
1685
|
+
visitFromNode(join.larg);
|
|
1686
|
+
visitFromNode(join.rarg);
|
|
1687
|
+
}
|
|
1688
|
+
};
|
|
1689
|
+
for (const fromNode of stmt?.fromClause ?? [])
|
|
1690
|
+
visitFromNode(fromNode);
|
|
1691
|
+
return names;
|
|
1692
|
+
}
|
|
1693
|
+
function rewritePgColumnSizeCompositeArgs(value, sourceNames) {
|
|
1694
|
+
if (!value || typeof value !== 'object')
|
|
1695
|
+
return value;
|
|
1696
|
+
if (Array.isArray(value))
|
|
1697
|
+
return value.map((item) => rewritePgColumnSizeCompositeArgs(item, sourceNames));
|
|
1698
|
+
if (value.FuncCall && functionName(value.FuncCall) === 'pg_column_size') {
|
|
1699
|
+
const arg = value.FuncCall.args?.[0];
|
|
1700
|
+
const fields = arg?.ColumnRef?.fields;
|
|
1701
|
+
if (Array.isArray(fields) && fields.length === 1) {
|
|
1702
|
+
const name = stringValue(fields[0]);
|
|
1703
|
+
if (name && sourceNames.has(name))
|
|
1704
|
+
return intConst(0);
|
|
1705
|
+
}
|
|
1706
|
+
else if (Array.isArray(fields) && fields.length === 2) {
|
|
1707
|
+
const schema = stringValue(fields[0]);
|
|
1708
|
+
const table = stringValue(fields[1]);
|
|
1709
|
+
if (schema && table && sourceNames.has(flattenSchemaName(schema, table)))
|
|
1710
|
+
return intConst(0);
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1714
|
+
value[key] = rewritePgColumnSizeCompositeArgs(child, sourceNames);
|
|
1715
|
+
}
|
|
1716
|
+
return value;
|
|
1717
|
+
}
|
|
1643
1718
|
function normalizeSelectStmt(stmt) {
|
|
1644
1719
|
const normalized = rewriteDistinctOnSelect(stmt);
|
|
1720
|
+
flattenSelectRangeVarQualifiers(normalized);
|
|
1721
|
+
rewritePgColumnSizeCompositeArgs(normalized, selectRangeVarNames(normalized));
|
|
1645
1722
|
rewriteRowToJsonSelect(normalized);
|
|
1646
1723
|
delete normalized.lockingClause;
|
|
1647
1724
|
return normalized;
|
|
@@ -2497,10 +2574,20 @@ function rewriteSkippedFunctionInvocationSelect(stmt, context) {
|
|
|
2497
2574
|
const name = functionName(funcCall);
|
|
2498
2575
|
if (!name || !skippedFunctionNames.has(name))
|
|
2499
2576
|
return false;
|
|
2577
|
+
if (name === 'schema_specs')
|
|
2578
|
+
return false;
|
|
2500
2579
|
target.name ??= functionDisplayName(funcCall) ?? name;
|
|
2501
2580
|
target.val = nullConst();
|
|
2502
2581
|
return true;
|
|
2503
2582
|
}
|
|
2583
|
+
function replaceSchemaSpecsFunctionCalls(sql) {
|
|
2584
|
+
let count = 0;
|
|
2585
|
+
const replaced = sql.replace(/(?:(?:"(?:[^"]|"")+"|[A-Za-z_][A-Za-z0-9_$]*)\s*\.\s*)?schema_specs\s*\(\s*\)/gi, () => {
|
|
2586
|
+
count++;
|
|
2587
|
+
return '?';
|
|
2588
|
+
});
|
|
2589
|
+
return { sql: replaced, count };
|
|
2590
|
+
}
|
|
2504
2591
|
function deparseStatement(version, stmt) {
|
|
2505
2592
|
return stripTrailingSemicolon(deparseSync({ version, stmts: [{ stmt }] }).trim());
|
|
2506
2593
|
}
|
|
@@ -3115,8 +3202,10 @@ function rewriteParsedStatement(version, rawStmt, context) {
|
|
|
3115
3202
|
for (const object of node.objects ?? []) {
|
|
3116
3203
|
const items = object.List?.items ?? [];
|
|
3117
3204
|
const name = stringValue(items[items.length - 1]);
|
|
3118
|
-
if (name)
|
|
3205
|
+
if (name) {
|
|
3206
|
+
context?.skippedFunctionNames?.delete(name.toLowerCase());
|
|
3119
3207
|
context?.triggerFunctions?.delete(name.toLowerCase());
|
|
3208
|
+
}
|
|
3120
3209
|
}
|
|
3121
3210
|
return null;
|
|
3122
3211
|
}
|
|
@@ -3265,12 +3354,15 @@ function rewriteParsedStatement(version, rawStmt, context) {
|
|
|
3265
3354
|
nodeType === 'IndexStmt' ||
|
|
3266
3355
|
nodeType === 'RenameStmt';
|
|
3267
3356
|
const isWrite = nodeType === 'DeleteStmt' || nodeType === 'InsertStmt' || nodeType === 'UpdateStmt';
|
|
3357
|
+
const usesPublishedSchemaFunction = context?.skippedFunctionNames?.has('schema_specs') &&
|
|
3358
|
+
containsFuncCall(stmt, 'schema_specs');
|
|
3268
3359
|
return {
|
|
3269
3360
|
sql: rewritten,
|
|
3270
3361
|
...(isDDL ? { isDDL } : null),
|
|
3271
3362
|
...(isWrite ? { isWrite } : null),
|
|
3272
3363
|
...(writeTable ? { writeTable } : null),
|
|
3273
3364
|
...(changeTracking ? { changeTracking } : null),
|
|
3365
|
+
...(usesPublishedSchemaFunction ? { usesPublishedSchemaFunction } : null),
|
|
3274
3366
|
...(context?.arrayParamNumbers?.size
|
|
3275
3367
|
? { arrayParamNumbers: new Set(context.arrayParamNumbers) }
|
|
3276
3368
|
: null),
|
|
@@ -3441,10 +3533,18 @@ function copySelectSQL(sql) {
|
|
|
3441
3533
|
const copy = parsed.stmts[0]?.stmt?.CopyStmt;
|
|
3442
3534
|
if (!copy?.query?.SelectStmt)
|
|
3443
3535
|
return null;
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3536
|
+
const binary = (copy.options ?? []).some((option) => {
|
|
3537
|
+
const def = option.DefElem;
|
|
3538
|
+
return (def?.defname?.toLowerCase?.() === 'format' &&
|
|
3539
|
+
stringValue(def.arg)?.toLowerCase() === 'binary');
|
|
3540
|
+
});
|
|
3541
|
+
return {
|
|
3542
|
+
sql: stripTrailingSemicolon(deparseSync({
|
|
3543
|
+
version: parsed.version,
|
|
3544
|
+
stmts: [{ stmt: { SelectStmt: copy.query.SelectStmt } }],
|
|
3545
|
+
}).trim()),
|
|
3546
|
+
binary,
|
|
3547
|
+
};
|
|
3448
3548
|
}
|
|
3449
3549
|
catch {
|
|
3450
3550
|
return null;
|
|
@@ -4463,9 +4563,10 @@ export class DoBackend {
|
|
|
4463
4563
|
return;
|
|
4464
4564
|
// single multi-row INSERT OR REPLACE per chunk. previously this was one
|
|
4465
4565
|
// HTTP roundtrip per row, which dominated boot when migrations touched
|
|
4466
|
-
// many columns.
|
|
4467
|
-
//
|
|
4468
|
-
|
|
4566
|
+
// many columns. Cloudflare DO SQLite has a lower host-param cap than
|
|
4567
|
+
// stock SQLite; 4 cols × 20 rows keeps metadata persistence comfortably
|
|
4568
|
+
// below that limit.
|
|
4569
|
+
const CHUNK = 20;
|
|
4469
4570
|
for (let i = 0; i < rows.length; i += CHUNK) {
|
|
4470
4571
|
const chunk = rows.slice(i, i + CHUNK);
|
|
4471
4572
|
const placeholders = chunk.map(() => '(?, ?, ?, ?)').join(', ');
|
|
@@ -4713,9 +4814,9 @@ export class DoBackend {
|
|
|
4713
4814
|
}
|
|
4714
4815
|
const copySelect = copySelectSQL(sql);
|
|
4715
4816
|
if (copySelect) {
|
|
4716
|
-
const rewrittenCopySelect = this.rewriteSQL(copySelect);
|
|
4817
|
+
const rewrittenCopySelect = this.rewriteSQL(copySelect.sql);
|
|
4717
4818
|
const result = await this.doExecResult(rewrittenCopySelect);
|
|
4718
|
-
return this.buildCopyResponse(result, copySelect);
|
|
4819
|
+
return this.buildCopyResponse(result, copySelect.sql, copySelect.binary);
|
|
4719
4820
|
}
|
|
4720
4821
|
// Prepare query
|
|
4721
4822
|
const rewrittenStatements = this.rewriteSQLStatements(sql);
|
|
@@ -4889,6 +4990,26 @@ export class DoBackend {
|
|
|
4889
4990
|
return projectReturningResult(result, tracking.returningProjection);
|
|
4890
4991
|
return result;
|
|
4891
4992
|
}
|
|
4993
|
+
async currentPublishedSchema(publications = [...this.publications.keys()]) {
|
|
4994
|
+
const infos = await this.publicationTableInfos(publications);
|
|
4995
|
+
return {
|
|
4996
|
+
tables: infos.map((info) => this.publicationTableSpec(info, publications)),
|
|
4997
|
+
indexes: infos.flatMap((info) => info.indexes),
|
|
4998
|
+
};
|
|
4999
|
+
}
|
|
5000
|
+
async materializePublishedSchemaFunctions(sql, statement, params = []) {
|
|
5001
|
+
if (!statement?.usesPublishedSchemaFunction)
|
|
5002
|
+
return { sql, params };
|
|
5003
|
+
const replaced = replaceSchemaSpecsFunctionCalls(sql);
|
|
5004
|
+
if (replaced.count === 0)
|
|
5005
|
+
return { sql, params };
|
|
5006
|
+
const schema = await this.currentPublishedSchema();
|
|
5007
|
+
const value = JSON.stringify(schema);
|
|
5008
|
+
return {
|
|
5009
|
+
sql: replaced.sql,
|
|
5010
|
+
params: [...params, ...Array.from({ length: replaced.count }, () => value)],
|
|
5011
|
+
};
|
|
5012
|
+
}
|
|
4892
5013
|
// ── Extended protocol handlers ──────────────────────────────────────────
|
|
4893
5014
|
handleParse(data) {
|
|
4894
5015
|
const sql = extractParseQuery(data);
|
|
@@ -5029,9 +5150,9 @@ export class DoBackend {
|
|
|
5029
5150
|
if (statement)
|
|
5030
5151
|
await this.snapshotTransactionWrite(statement);
|
|
5031
5152
|
const tracking = statement ? this.trackingForStatement(statement) : undefined;
|
|
5032
|
-
const
|
|
5033
|
-
const
|
|
5034
|
-
const result = await this.doExecResult(
|
|
5153
|
+
const bound = this.sqliteBoundSQL(tracking?.returningSQL ?? sql, portal.params, portal.arrayParamNumbers, portal.jsonParamNumbers, portal.timestampParamNumbers, portal.epochMillisParamNumbers, portal.booleanParamNumbers);
|
|
5154
|
+
const exec = await this.materializePublishedSchemaFunctions(bound.sql, statement, bound.params);
|
|
5155
|
+
const result = await this.doExecResult(exec.sql, exec.params, tracking ? this.trackingRequest(tracking) : undefined);
|
|
5035
5156
|
if (portal.schemaColumns?.length) {
|
|
5036
5157
|
await this.applyStatementMetadata([{ sql, schemaColumns: portal.schemaColumns }]);
|
|
5037
5158
|
}
|
|
@@ -5111,6 +5232,12 @@ export class DoBackend {
|
|
|
5111
5232
|
if (isCatalogQuery(rewritten))
|
|
5112
5233
|
return (await this.handleCatalogQuery(rewritten)).rows;
|
|
5113
5234
|
const statement = statements.length === 1 ? statements[0] : undefined;
|
|
5235
|
+
if (statements.some((item) => item.usesPublishedSchemaFunction)) {
|
|
5236
|
+
const result = await this.executeRewrittenStatements(statements);
|
|
5237
|
+
await this.applyStatementMetadata(statements);
|
|
5238
|
+
const tracking = statement ? this.trackingForStatement(statement) : undefined;
|
|
5239
|
+
return this.visibleResultForTracking(result, tracking).rows;
|
|
5240
|
+
}
|
|
5114
5241
|
if (statement)
|
|
5115
5242
|
await this.snapshotTransactionWrite(statement);
|
|
5116
5243
|
const tracking = statement ? this.trackingForStatement(statement) : undefined;
|
|
@@ -5180,7 +5307,8 @@ export class DoBackend {
|
|
|
5180
5307
|
const execBound = tracking
|
|
5181
5308
|
? this.sqliteBoundSQL(tracking.returningSQL, params, arrayParamNumbers, jsonParamNumbers, timestampParamNumbers, epochMillisParamNumbers, booleanParamNumbers)
|
|
5182
5309
|
: bound;
|
|
5183
|
-
const
|
|
5310
|
+
const exec = await this.materializePublishedSchemaFunctions(execBound.sql, statement, execBound.params);
|
|
5311
|
+
const result = await this.doExecResult(exec.sql, exec.params, tracking ? this.trackingRequest(tracking) : undefined);
|
|
5184
5312
|
await this.applyStatementMetadata(statements);
|
|
5185
5313
|
return { rows: this.visibleResultForTracking(result, tracking).rows };
|
|
5186
5314
|
}
|
|
@@ -5219,6 +5347,7 @@ export class DoBackend {
|
|
|
5219
5347
|
return (statements.length > 0 &&
|
|
5220
5348
|
statements.every((statement) => statement.sql.trim() &&
|
|
5221
5349
|
!statement.isDDL &&
|
|
5350
|
+
!statement.usesPublishedSchemaFunction &&
|
|
5222
5351
|
!statement.schemaColumns?.length &&
|
|
5223
5352
|
!statement.schemaMetadataChanges?.length &&
|
|
5224
5353
|
!statement.publicationChanges?.length));
|
|
@@ -5425,7 +5554,8 @@ export class DoBackend {
|
|
|
5425
5554
|
return { rows: [], columns: [] };
|
|
5426
5555
|
await this.snapshotTransactionWrite(statement);
|
|
5427
5556
|
const tracking = this.trackingForStatement(statement);
|
|
5428
|
-
|
|
5557
|
+
const exec = await this.materializePublishedSchemaFunctions(tracking?.returningSQL ?? statement.sql, statement);
|
|
5558
|
+
return this.doExecResult(exec.sql, exec.params, tracking ? this.trackingRequest(tracking) : undefined);
|
|
5429
5559
|
}
|
|
5430
5560
|
async executeRewrittenStatements(statements) {
|
|
5431
5561
|
let result = { rows: [], columns: [] };
|
|
@@ -5469,12 +5599,16 @@ export class DoBackend {
|
|
|
5469
5599
|
}
|
|
5470
5600
|
}
|
|
5471
5601
|
const tracking = this.trackingForStatement(item);
|
|
5602
|
+
const exec = await this.materializePublishedSchemaFunctions(tracking?.returningSQL ?? item.sql, item);
|
|
5472
5603
|
sqls.push(tracking
|
|
5473
5604
|
? {
|
|
5474
|
-
sql:
|
|
5605
|
+
sql: exec.sql,
|
|
5606
|
+
...(exec.params.length ? { params: exec.params } : null),
|
|
5475
5607
|
track: this.trackingRequest(tracking),
|
|
5476
5608
|
}
|
|
5477
|
-
:
|
|
5609
|
+
: exec.params.length
|
|
5610
|
+
? { sql: exec.sql, params: exec.params }
|
|
5611
|
+
: exec.sql);
|
|
5478
5612
|
}
|
|
5479
5613
|
await flush();
|
|
5480
5614
|
}
|
|
@@ -5944,6 +6078,27 @@ export class DoBackend {
|
|
|
5944
6078
|
fields: [{ name: 'indexes', oid: PG_TYPE_JSON }],
|
|
5945
6079
|
};
|
|
5946
6080
|
}
|
|
6081
|
+
async publishedSchemaResult(select) {
|
|
6082
|
+
const fields = selectTargetFields(select);
|
|
6083
|
+
if (!fields.some((field) => field.name === 'publishedSchema'))
|
|
6084
|
+
return null;
|
|
6085
|
+
if (!selectReferencesTable(select, 'pg_publication_tables'))
|
|
6086
|
+
return null;
|
|
6087
|
+
const requested = stringFilterValues(select, 'pubname');
|
|
6088
|
+
const publications = requested.length ? requested : [...this.publications.keys()];
|
|
6089
|
+
const infos = await this.publicationTableInfos(publications);
|
|
6090
|
+
return {
|
|
6091
|
+
rows: [
|
|
6092
|
+
{
|
|
6093
|
+
publishedSchema: {
|
|
6094
|
+
tables: infos.map((info) => this.publicationTableSpec(info, publications)),
|
|
6095
|
+
indexes: infos.flatMap((info) => info.indexes),
|
|
6096
|
+
},
|
|
6097
|
+
},
|
|
6098
|
+
],
|
|
6099
|
+
fields: [{ name: 'publishedSchema', oid: PG_TYPE_JSON }],
|
|
6100
|
+
};
|
|
6101
|
+
}
|
|
5947
6102
|
pgTypeArrayResult(select) {
|
|
5948
6103
|
if (!selectReferencesTable(select, 'pg_type'))
|
|
5949
6104
|
return null;
|
|
@@ -6279,7 +6434,58 @@ export class DoBackend {
|
|
|
6279
6434
|
}
|
|
6280
6435
|
return out;
|
|
6281
6436
|
}
|
|
6282
|
-
|
|
6437
|
+
copyBinaryValue(column, value, field) {
|
|
6438
|
+
if (value === null || value === undefined)
|
|
6439
|
+
return null;
|
|
6440
|
+
const oid = field?.oid;
|
|
6441
|
+
if (oid === PG_TYPE_BOOL)
|
|
6442
|
+
return new Uint8Array([value === true || value === 1 ? 1 : 0]);
|
|
6443
|
+
if (oid === PG_TYPE_INT2)
|
|
6444
|
+
return i16(Number(value));
|
|
6445
|
+
if (oid === PG_TYPE_INT4)
|
|
6446
|
+
return i32(Number(value));
|
|
6447
|
+
if (oid === PG_TYPE_INT8)
|
|
6448
|
+
return i64(BigInt(value));
|
|
6449
|
+
if (oid === PG_TYPE_FLOAT8) {
|
|
6450
|
+
const buf = new ArrayBuffer(8);
|
|
6451
|
+
new DataView(buf).setFloat64(0, Number(value));
|
|
6452
|
+
return new Uint8Array(buf);
|
|
6453
|
+
}
|
|
6454
|
+
if (oid === PG_TYPE_TIMESTAMP || oid === PG_TYPE_TIMESTAMPTZ) {
|
|
6455
|
+
const millis = timestampMillisValue(value);
|
|
6456
|
+
const finite = typeof millis === 'number' && Number.isFinite(millis) ? millis : 0;
|
|
6457
|
+
return i64(BigInt(Math.round((finite - 946684800000) * 1000)));
|
|
6458
|
+
}
|
|
6459
|
+
if (oid === PG_TYPE_JSONB) {
|
|
6460
|
+
const json = typeof value === 'string' ? value : JSON.stringify(sqliteJsonParamValue(value));
|
|
6461
|
+
return concat(new Uint8Array([1]), textEncoder.encode(json));
|
|
6462
|
+
}
|
|
6463
|
+
if (oid === PG_TYPE_BYTEA && value instanceof Uint8Array)
|
|
6464
|
+
return value;
|
|
6465
|
+
if (oid === PG_TYPE_JSON) {
|
|
6466
|
+
const json = typeof value === 'string' ? value : JSON.stringify(sqliteJsonParamValue(value));
|
|
6467
|
+
return textEncoder.encode(json);
|
|
6468
|
+
}
|
|
6469
|
+
return textEncoder.encode(this.copyTextValue(column, value, field));
|
|
6470
|
+
}
|
|
6471
|
+
copyBinaryRow(columns, row, fields) {
|
|
6472
|
+
const parts = [i16(columns.length)];
|
|
6473
|
+
for (const [index, column] of columns.entries()) {
|
|
6474
|
+
const value = this.copyBinaryValue(column, row[column], fields[index]);
|
|
6475
|
+
if (value === null) {
|
|
6476
|
+
parts.push(i32(-1));
|
|
6477
|
+
}
|
|
6478
|
+
else {
|
|
6479
|
+
parts.push(i32(value.length), value);
|
|
6480
|
+
}
|
|
6481
|
+
}
|
|
6482
|
+
return concat(...parts);
|
|
6483
|
+
}
|
|
6484
|
+
buildBinaryCopyResponse(result, columns, fields) {
|
|
6485
|
+
const header = concat(new Uint8Array([80, 71, 67, 79, 80, 89, 10, 255, 13, 10, 0]), i32(0), i32(0));
|
|
6486
|
+
return concat(buildCopyOutResponse(columns.length, true), buildCopyDataBytes(header), ...result.rows.map((row) => buildCopyDataBytes(this.copyBinaryRow(columns, row, fields))), buildCopyDataBytes(i16(-1)), buildCopyDone(), buildCommandComplete(`COPY ${result.rows.length}`), this.readyForQuery());
|
|
6487
|
+
}
|
|
6488
|
+
buildCopyResponse(result, sql = '', binary = false) {
|
|
6283
6489
|
const columns = result.columns.length > 0
|
|
6284
6490
|
? result.columns
|
|
6285
6491
|
: result.rows.length > 0
|
|
@@ -6289,6 +6495,8 @@ export class DoBackend {
|
|
|
6289
6495
|
name,
|
|
6290
6496
|
oid: sql ? this.fieldMetadataForResultColumn(sql, name)?.oid : undefined,
|
|
6291
6497
|
}));
|
|
6498
|
+
if (binary)
|
|
6499
|
+
return this.buildBinaryCopyResponse(result, columns, fields);
|
|
6292
6500
|
return concat(buildCopyOutResponse(columns.length), ...result.rows.map((row) => buildCopyData(`${columns.map((column, index) => this.copyTextValue(column, row[column], fields[index])).join('\t')}\n`)), buildCopyDone(), buildCommandComplete(`COPY ${result.rows.length}`), this.readyForQuery());
|
|
6293
6501
|
}
|
|
6294
6502
|
fieldMetadataForResultColumn(sql, column) {
|
|
@@ -6435,6 +6643,7 @@ export class DoBackend {
|
|
|
6435
6643
|
(await this.informationSchemaKeyColumnsResult(select)) ??
|
|
6436
6644
|
(await this.informationSchemaColumnsResult(select)) ??
|
|
6437
6645
|
this.pgTypeArrayResult(select) ??
|
|
6646
|
+
(await this.publishedSchemaResult(select)) ??
|
|
6438
6647
|
(await this.publishedTablesResult(select)) ??
|
|
6439
6648
|
(await this.publishedIndexesResult(select)) ??
|
|
6440
6649
|
(await this.pgPublicationTablesResult(select)) ??
|