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.
@@ -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 = 49;
13
+ const SYNC_PROTOCOL_VERSION = 50;
14
14
  const NUM_MUTATIONS = 100;
15
15
  // test schema
16
16
  const CLIENT_SCHEMA = {
@@ -9,7 +9,7 @@ import { DurableObject } from 'cloudflare:workers';
9
9
  * for zero-cache.
10
10
  *
11
11
  * Modes:
12
- * WS /sync/v49/connect — bespoke Zero sync protocol (dev/protocol testing)
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":"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"}
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
- return stripTrailingSemicolon(deparseSync({
3445
- version: parsed.version,
3446
- stmts: [{ stmt: { SelectStmt: copy.query.SelectStmt } }],
3447
- }).trim());
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. SQLite caps host params near 999; 4 cols × 200 rows
4467
- // leaves comfortable headroom.
4468
- const CHUNK = 200;
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 execSql = tracking?.returningSQL ?? sql;
5033
- const bound = this.sqliteBoundSQL(execSql, portal.params, portal.arrayParamNumbers, portal.jsonParamNumbers, portal.timestampParamNumbers, portal.epochMillisParamNumbers, portal.booleanParamNumbers);
5034
- const result = await this.doExecResult(bound.sql, bound.params, tracking ? this.trackingRequest(tracking) : undefined);
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 result = await this.doExecResult(execBound.sql, execBound.params, tracking ? this.trackingRequest(tracking) : undefined);
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
- return this.doExecResult(tracking?.returningSQL ?? statement.sql, undefined, tracking ? this.trackingRequest(tracking) : undefined);
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: tracking.returningSQL,
5605
+ sql: exec.sql,
5606
+ ...(exec.params.length ? { params: exec.params } : null),
5475
5607
  track: this.trackingRequest(tracking),
5476
5608
  }
5477
- : item.sql);
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
- buildCopyResponse(result, sql = '') {
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)) ??