@rex0220/kintone-sql-tools 3.46.0 → 3.47.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-cli/ksql.js CHANGED
@@ -11619,6 +11619,42 @@ var LOCAL_COLLECTION_TYPES = /* @__PURE__ */ new Set([
11619
11619
  function nativeWhereOperatorsForType(fieldType) {
11620
11620
  return NATIVE_OPERATORS.get(fieldType) ?? /* @__PURE__ */ new Set();
11621
11621
  }
11622
+ function normalizeChoiceEquality(where, resolveField2) {
11623
+ const rewrites = [];
11624
+ const visit = (node) => {
11625
+ if (node.type === "BINARY") {
11626
+ if ((node.op === "=" || node.op === "!=" || node.op === "<>") && node.left.type === "FIELD" && node.right.type === "STRING" && node.right.value !== "") {
11627
+ const semantics = resolveField2(node.left);
11628
+ if (semantics !== void 0 && semantics.compareMode === "option" && LOCAL_SCALAR_TYPES.has(semantics.fieldType) && nativeWhereOperatorsForType(semantics.fieldType).has("in") && semantics.optionOrder?.has(node.right.value) === true) {
11629
+ const normalizedOperator = node.op === "=" ? "IN" : "NOT_IN";
11630
+ rewrites.push({
11631
+ field: node.left,
11632
+ originalOperator: node.op,
11633
+ normalizedOperator,
11634
+ value: node.right.value
11635
+ });
11636
+ return {
11637
+ ...node,
11638
+ op: normalizedOperator,
11639
+ right: { type: "IN_LIST", values: [node.right] }
11640
+ };
11641
+ }
11642
+ }
11643
+ return node;
11644
+ }
11645
+ if (node.type === "LOGICAL") {
11646
+ const left = visit(node.left);
11647
+ const right = visit(node.right);
11648
+ return left === node.left && right === node.right ? node : { ...node, left, right };
11649
+ }
11650
+ if (node.type === "GROUP" || node.type === "NOT") {
11651
+ const expr = visit(node.expr);
11652
+ return expr === node.expr ? node : { ...node, expr };
11653
+ }
11654
+ return node;
11655
+ };
11656
+ return { normalizedWhere: visit(where), rewrites };
11657
+ }
11622
11658
  function classifyWhereCapability(where, resolveField2) {
11623
11659
  if (where === null) {
11624
11660
  return { capability: "EXACT_PUSHDOWN", reasons: [{ code: "WHERE_EXACT" }] };
@@ -16399,18 +16435,18 @@ async function assertRelativeDateExecutionPlan(stmt, client, cacheContext) {
16399
16435
  }
16400
16436
  async function resolveRelativeDateExecutionPlan(stmt, client, cacheContext) {
16401
16437
  return buildRelativeDatePushdownPlan(stmt, {
16402
- select: (select) => resolveSelectWhereCapability(select, client, cacheContext),
16438
+ select: async (select) => {
16439
+ const { resolver } = await normalizeSelectChoiceEquality(select, client, cacheContext);
16440
+ return classifyWhereCapability(select.where, resolver);
16441
+ },
16403
16442
  dml: (dml) => resolveDmlWhereCapability(dml, client, cacheContext),
16404
16443
  prefilterDecomposition: async (select) => {
16405
16444
  if (select.where === null) return null;
16406
- const resolver = await buildWhereFieldSemanticsResolver(
16407
- select,
16408
- client,
16409
- cacheContext
16410
- );
16445
+ const { resolver } = await normalizeSelectChoiceEquality(select, client, cacheContext);
16411
16446
  return decomposeRelativeDatePrefilter(select, resolver);
16412
16447
  },
16413
16448
  joinServerFunctionPlan: async (select) => {
16449
+ await normalizeSelectChoiceEquality(select, client, cacheContext);
16414
16450
  const metadata = await loadTypedPushdownMeta(select, client, cacheContext);
16415
16451
  const runtimePlan = buildRuntimeJoinPushdownPlan(select, metadata);
16416
16452
  if (runtimePlan === null) return null;
@@ -17480,7 +17516,10 @@ async function buildWhereFieldSemanticsResolver(stmt, client, cacheContext, mate
17480
17516
  if (node === null || typeof node !== "object") return;
17481
17517
  const value = node;
17482
17518
  if (value["type"] === "SELECT") return;
17483
- if (value["type"] === "BINARY" && [">", "<", ">=", "<="].includes(String(value["op"]))) {
17519
+ const operator = String(value["op"]);
17520
+ const right = value["right"];
17521
+ const needsOptionExistence = ["=", "!=", "<>"].includes(operator) && right?.["type"] === "STRING" && right["value"] !== "";
17522
+ if (value["type"] === "BINARY" && ([">", "<", ">=", "<="].includes(operator) || needsOptionExistence)) {
17484
17523
  const left = value["left"];
17485
17524
  if (left?.["type"] === "FIELD" && typeof left["field"] === "string") {
17486
17525
  orderedFields.add(left["field"]);
@@ -17547,6 +17586,49 @@ async function buildWhereFieldSemanticsResolver(stmt, client, cacheContext, mate
17547
17586
  return matches.length > 1 ? syntheticSemantics("string") : void 0;
17548
17587
  };
17549
17588
  }
17589
+ var choiceEqualityRewritesBySelect = /* @__PURE__ */ new WeakMap();
17590
+ async function normalizeSelectChoiceEquality(stmt, client, cacheContext, materializedTables, forcePhysicalMetadata = false) {
17591
+ const resolver = await buildWhereFieldSemanticsResolver(
17592
+ stmt,
17593
+ client,
17594
+ cacheContext,
17595
+ materializedTables,
17596
+ forcePhysicalMetadata
17597
+ );
17598
+ if (stmt.where === null) return { resolver, rewrites: [] };
17599
+ const normalization = normalizeChoiceEquality(stmt.where, resolver);
17600
+ stmt.where = normalization.normalizedWhere;
17601
+ if (normalization.rewrites.length > 0) {
17602
+ choiceEqualityRewritesBySelect.set(stmt, normalization.rewrites);
17603
+ }
17604
+ return { resolver, rewrites: normalization.rewrites };
17605
+ }
17606
+ function hasDefaultRangeAggregateWindow(stmt) {
17607
+ return stmt.columns.some(
17608
+ (column) => column.type === "WINDOW_COL" && column.windowKind === "AGGREGATE" && column.orderBy.length > 0 && column.frame?.source === "DEFAULT"
17609
+ );
17610
+ }
17611
+ function collectDefaultRangeWindowWarnings(stmt, resolveField2, context) {
17612
+ const canProveSinglePhysicalInput = context === "DIRECT" && stmt.joins.length === 0 && stmt.from.cteName === null && stmt.from.subtableCode == null;
17613
+ const warnings = [];
17614
+ for (const column of stmt.columns) {
17615
+ if (column.type !== "WINDOW_COL" || column.windowKind !== "AGGREGATE" || column.orderBy.length === 0 || column.frame?.source !== "DEFAULT") continue;
17616
+ const hasTotalOrderKey = canProveSinglePhysicalInput && column.orderBy.some((item) => {
17617
+ if (item.key.type !== "FIELD_NAME") return false;
17618
+ const ref = aggregateFieldRef(item.key.name);
17619
+ return ref.field === "$id" || resolveField2(ref)?.fieldType === "RECORD_NUMBER";
17620
+ });
17621
+ if (hasTotalOrderKey) continue;
17622
+ warnings.push(
17623
+ `${column.alias} \u306F\u65E2\u5B9A\u30D5\u30EC\u30FC\u30E0\uFF08RANGE\uFF09\u3067\u8A55\u4FA1\u3055\u308C\u307E\u3059\u3002ORDER BY \u306E\u5024\u304C\u540C\u3058\u884C\u306F\u3059\u3079\u3066\u540C\u3058\u5024\u306B\u306A\u308A\u307E\u3059\u3002\u884C\u3054\u3068\u306E\u5024\u304C\u5FC5\u8981\u306A\u3089 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW \u3092\u660E\u793A\u3059\u308B\u304B\u3001ORDER BY \u306B\u30EC\u30B3\u30FC\u30C9\u756A\u53F7\u306A\u3069\u306E\u30BF\u30A4\u30D6\u30EC\u30FC\u30AF\u30AD\u30FC\u3092\u8DB3\u3057\u3066\u304F\u3060\u3055\u3044\u3002`
17624
+ );
17625
+ }
17626
+ return warnings;
17627
+ }
17628
+ function mergeSelectWarnings(result, additional) {
17629
+ if (additional.length === 0) return result;
17630
+ return { ...result, warnings: [.../* @__PURE__ */ new Set([...result.warnings ?? [], ...additional])] };
17631
+ }
17550
17632
  function selectCaseConditionsNeedFieldMetadata(stmt) {
17551
17633
  const visit = (value) => {
17552
17634
  if (value === null || typeof value !== "object") return false;
@@ -17603,11 +17685,6 @@ function buildHavingFieldSemanticsResolver(stmt, rowResolver) {
17603
17685
  }
17604
17686
  return (field) => field.tableAlias === null && aliases.has(field.field) ? aliases.get(field.field) : rowResolver(field);
17605
17687
  }
17606
- async function resolveSelectWhereCapability(stmt, client, cacheContext, materializedTables) {
17607
- if (stmt.where === null) return classifyWhereCapability(null, () => void 0);
17608
- const resolver = await buildWhereFieldSemanticsResolver(stmt, client, cacheContext, materializedTables);
17609
- return classifyWhereCapability(stmt.where, resolver);
17610
- }
17611
17688
  function formatWhereCapabilityFailure(result) {
17612
17689
  const reason = result.reasons.find(
17613
17690
  (candidate) => candidate.code === "WHERE_FIELD_UNRESOLVED" || candidate.code === "WHERE_OPERATOR_UNSUPPORTED" || candidate.code === "WHERE_OPERATOR_INVALID_FOR_FIELD_TYPE"
@@ -17652,7 +17729,7 @@ async function assertDmlWhereCapability(stmt, client, cacheContext) {
17652
17729
  );
17653
17730
  }
17654
17731
  }
17655
- async function executeSelect(stmt, client, options, cacheContext, cteCache, captureColumnMeta = false, forLibraryCapture = false) {
17732
+ async function executeSelect(stmt, client, options, cacheContext, cteCache, captureColumnMeta = false, forLibraryCapture = false, windowWarningContext = "DIRECT") {
17656
17733
  let result;
17657
17734
  await validateSelectGroupingPlanning(stmt, client, cacheContext, cteCache);
17658
17735
  if (isNoFromSelect(stmt)) {
@@ -17665,6 +17742,18 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
17665
17742
  }
17666
17743
  return result;
17667
17744
  }
17745
+ const { resolver: fieldSemanticsResolver } = await normalizeSelectChoiceEquality(
17746
+ stmt,
17747
+ client,
17748
+ cacheContext,
17749
+ cteCache,
17750
+ hasDefaultRangeAggregateWindow(stmt)
17751
+ );
17752
+ const defaultRangeWarnings = collectDefaultRangeWindowWarnings(
17753
+ stmt,
17754
+ fieldSemanticsResolver,
17755
+ windowWarningContext
17756
+ );
17668
17757
  const plainGroupByPlan = await buildRuntimePlainGroupByPlan(
17669
17758
  stmt,
17670
17759
  client,
@@ -17672,7 +17761,10 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
17672
17761
  cteCache
17673
17762
  );
17674
17763
  await resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache);
17675
- const whereCapability = rememberSelectWhereCapability(stmt, await resolveSelectWhereCapability(stmt, client, cacheContext, cteCache));
17764
+ const whereCapability = rememberSelectWhereCapability(
17765
+ stmt,
17766
+ classifyWhereCapability(stmt.where, fieldSemanticsResolver)
17767
+ );
17676
17768
  if (whereCapability.capability === "UNSUPPORTED") {
17677
17769
  throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(whereCapability)}).`);
17678
17770
  }
@@ -17759,7 +17851,7 @@ async function executeSelect(stmt, client, options, cacheContext, cteCache, capt
17759
17851
  await inferSelectColumnMeta(stmt, result.columns, client, cacheContext, cteCache, forLibraryCapture)
17760
17852
  );
17761
17853
  }
17762
- return result;
17854
+ return mergeSelectWarnings(result, defaultRangeWarnings);
17763
17855
  }
17764
17856
  async function buildRuntimePlainGroupByPlan(stmt, client, cacheContext, materializedTables) {
17765
17857
  if (stmt.groupBy.length === 0) return void 0;
@@ -19270,7 +19362,8 @@ async function executeUnion(stmt, client, options, cacheContext, captureColumnMe
19270
19362
  cacheContext,
19271
19363
  void 0,
19272
19364
  captureColumnMeta,
19273
- forLibraryCapture
19365
+ forLibraryCapture,
19366
+ "DERIVED"
19274
19367
  ),
19275
19368
  executeSelect(
19276
19369
  markCountTotalCountRoot(stmt.right),
@@ -19279,7 +19372,8 @@ async function executeUnion(stmt, client, options, cacheContext, captureColumnMe
19279
19372
  cacheContext,
19280
19373
  void 0,
19281
19374
  captureColumnMeta,
19282
- forLibraryCapture
19375
+ forLibraryCapture,
19376
+ "DERIVED"
19283
19377
  )
19284
19378
  ]);
19285
19379
  const leftCols = leftResult.columns;
@@ -19293,7 +19387,17 @@ async function executeUnion(stmt, client, options, cacheContext, captureColumnMe
19293
19387
  });
19294
19388
  const combined = [...leftResult.rows, ...remappedRight];
19295
19389
  const rows = stmt.all ? combined : deduplicateRows(combined, leftCols);
19296
- const result = { type: "SELECT", rows, columns: leftCols, rowCount: rows.length };
19390
+ const warnings = [.../* @__PURE__ */ new Set([
19391
+ ...leftResult.warnings ?? [],
19392
+ ...rightResult.warnings ?? []
19393
+ ])];
19394
+ const result = {
19395
+ type: "SELECT",
19396
+ rows,
19397
+ columns: leftCols,
19398
+ rowCount: rows.length,
19399
+ warnings
19400
+ };
19297
19401
  if (captureColumnMeta) {
19298
19402
  materializedMetaBySelectResult.set(result, mergeUnionColumnMeta(leftResult, rightResult));
19299
19403
  }
@@ -19311,25 +19415,44 @@ function deduplicateRows(rows, columns) {
19311
19415
  }
19312
19416
  async function executeWith(stmt, client, options, cacheContext, seed, captureColumnMeta = false) {
19313
19417
  if ((seed == null || seed.size === 0) && canInlineSingleCte(stmt)) {
19314
- return executeSelect(buildInlinedQuery(stmt), client, options, cacheContext, void 0, captureColumnMeta, false);
19418
+ return executeSelect(
19419
+ buildInlinedQuery(stmt),
19420
+ client,
19421
+ options,
19422
+ cacheContext,
19423
+ void 0,
19424
+ captureColumnMeta,
19425
+ false,
19426
+ "DERIVED"
19427
+ );
19315
19428
  }
19316
19429
  const cteCache = new Map(seed ?? []);
19430
+ const warnings = /* @__PURE__ */ new Set();
19317
19431
  for (const cte of stmt.ctes) {
19318
- let result;
19432
+ let result2;
19319
19433
  if (cte.query.type === "SHOW_APPS") {
19320
- result = await executeShowApps(client);
19434
+ result2 = await executeShowApps(client);
19321
19435
  } else if (cte.query.type === "DESCRIBE") {
19322
- result = await executeDescribe(cte.query, client, cacheContext);
19436
+ result2 = await executeDescribe(cte.query, client, cacheContext);
19323
19437
  } else {
19324
- result = await executeQueryWithCte(cte.query, client, options, cteCache, cacheContext, true);
19438
+ result2 = await executeQueryWithCte(cte.query, client, options, cteCache, cacheContext, true);
19325
19439
  }
19440
+ for (const warning of result2.warnings ?? []) warnings.add(warning);
19326
19441
  cteCache.set(cte.name, {
19327
- rows: result.rows,
19328
- columns: result.columns,
19329
- columnMeta: materializedMetaBySelectResult.get(result)
19442
+ rows: result2.rows,
19443
+ columns: result2.columns,
19444
+ columnMeta: materializedMetaBySelectResult.get(result2)
19330
19445
  });
19331
19446
  }
19332
- return executeQueryWithCte(stmt.query, client, options, cteCache, cacheContext, captureColumnMeta);
19447
+ const result = await executeQueryWithCte(
19448
+ stmt.query,
19449
+ client,
19450
+ options,
19451
+ cteCache,
19452
+ cacheContext,
19453
+ captureColumnMeta
19454
+ );
19455
+ return mergeSelectWarnings(result, [...warnings]);
19333
19456
  }
19334
19457
  async function executeQueryWithCte(query, client, options, cteCache, cacheContext, captureColumnMeta = false, b86PreflightComplete = false) {
19335
19458
  if (!b86PreflightComplete) {
@@ -19351,7 +19474,17 @@ async function executeQueryWithCte(query, client, options, cteCache, cacheContex
19351
19474
  });
19352
19475
  const combined = [...leftResult.rows, ...remapped];
19353
19476
  const rows = query.all ? combined : deduplicateRows(combined, leftCols);
19354
- const result2 = { type: "SELECT", rows, columns: leftCols, rowCount: rows.length };
19477
+ const warnings = [.../* @__PURE__ */ new Set([
19478
+ ...leftResult.warnings ?? [],
19479
+ ...rightResult.warnings ?? []
19480
+ ])];
19481
+ const result2 = {
19482
+ type: "SELECT",
19483
+ rows,
19484
+ columns: leftCols,
19485
+ rowCount: rows.length,
19486
+ warnings
19487
+ };
19355
19488
  if (captureColumnMeta) {
19356
19489
  materializedMetaBySelectResult.set(result2, mergeUnionColumnMeta(leftResult, rightResult));
19357
19490
  }
@@ -19359,7 +19492,16 @@ async function executeQueryWithCte(query, client, options, cteCache, cacheContex
19359
19492
  }
19360
19493
  const hasCteRef = query.from.cteName != null && query.from.cteName !== NO_FROM_CTE_NAME || query.joins.some((j) => j.table.cteName != null);
19361
19494
  if (!hasCteRef) {
19362
- return executeSelect(query, client, options, cacheContext, cteCache, captureColumnMeta, false);
19495
+ return executeSelect(
19496
+ query,
19497
+ client,
19498
+ options,
19499
+ cacheContext,
19500
+ cteCache,
19501
+ captureColumnMeta,
19502
+ false,
19503
+ "DERIVED"
19504
+ );
19363
19505
  }
19364
19506
  const result = await executeFullScanWithCte(query, client, options, cteCache, cacheContext);
19365
19507
  if (captureColumnMeta) {
@@ -19407,6 +19549,18 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
19407
19549
  for (const table of [stmt.from, ...stmt.joins.map((join2) => join2.table)]) {
19408
19550
  if (table.cteName !== null) requireMaterializedTable(table.cteName);
19409
19551
  }
19552
+ const { resolver: choiceAndWindowResolver } = await normalizeSelectChoiceEquality(
19553
+ stmt,
19554
+ client,
19555
+ cacheContext,
19556
+ cteCache,
19557
+ hasDefaultRangeAggregateWindow(stmt)
19558
+ );
19559
+ const defaultRangeWarnings = collectDefaultRangeWindowWarnings(
19560
+ stmt,
19561
+ choiceAndWindowResolver,
19562
+ "DERIVED"
19563
+ );
19410
19564
  const maxRecords = options.maxRecords ?? 1e4;
19411
19565
  const warnings = /* @__PURE__ */ new Set();
19412
19566
  const parallel = options.fetchParallel ?? 1;
@@ -19415,7 +19569,7 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
19415
19569
  resolveSubqueries(stmt.having, client, options, cacheContext, cteCache),
19416
19570
  resolveSelectCaseSubqueries(stmt, client, options, cacheContext, cteCache)
19417
19571
  ]);
19418
- const whereCapability = await resolveSelectWhereCapability(stmt, client, cacheContext, cteCache);
19572
+ const whereCapability = classifyWhereCapability(stmt.where, choiceAndWindowResolver);
19419
19573
  if (whereCapability.capability === "UNSUPPORTED") {
19420
19574
  throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(whereCapability)}).`);
19421
19575
  }
@@ -19544,7 +19698,10 @@ async function executeFullScanWithCte(stmt, client, options, cteCache, cacheCont
19544
19698
  client,
19545
19699
  cacheContext
19546
19700
  );
19547
- return { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] };
19701
+ return mergeSelectWarnings(
19702
+ { type: "SELECT", rows, columns, rowCount: rows.length, warnings: [...warnings] },
19703
+ defaultRangeWarnings
19704
+ );
19548
19705
  }
19549
19706
  async function restoreEmptyWildcardColumns(stmt, rows, columns, client, cacheContext) {
19550
19707
  if (rows.length !== 0 || columns.length !== 0 || stmt.columns.length !== 1 || stmt.columns[0].type !== "WILDCARD" || stmt.joins.length !== 0 || stmt.from.cteName !== null) {
@@ -22958,6 +23115,7 @@ async function resolveScalarColumns(columns, client, options, cacheContext, cteC
22958
23115
  return cache;
22959
23116
  }
22960
23117
  var explainJoinPushdownPlans = /* @__PURE__ */ new WeakMap();
23118
+ var explainChoiceEqualityRewrites = /* @__PURE__ */ new WeakMap();
22961
23119
  var validateExplainInfo = /* @__PURE__ */ new WeakMap();
22962
23120
  var applyParentExplainPlan = /* @__PURE__ */ new WeakMap();
22963
23121
  async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords = 1e4, relativeDatePlan) {
@@ -23013,7 +23171,13 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
23013
23171
  if (needsWhereSchema || select.orderBy.length > 0 || select.columns.some((column) => column.type === "WINDOW_COL" && column.orderBy.length > 0)) {
23014
23172
  physicalApps.forEach((appId) => fieldApps.add(appId));
23015
23173
  }
23016
- const capability = await resolveSelectWhereCapability(select, tracedClient, cacheContext);
23174
+ const { resolver, rewrites } = await normalizeSelectChoiceEquality(
23175
+ select,
23176
+ tracedClient,
23177
+ cacheContext
23178
+ );
23179
+ if (rewrites.length > 0) explainChoiceEqualityRewrites.set(select, rewrites);
23180
+ const capability = classifyWhereCapability(select.where, resolver);
23017
23181
  const relativeNode = relativeNodeFor(select);
23018
23182
  if (capability.capability === "UNSUPPORTED" && !relativeNode) {
23019
23183
  throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
@@ -23125,7 +23289,13 @@ async function buildExplainWhereAnalysis(query, client, cacheContext, maxRecords
23125
23289
  await visit(query);
23126
23290
  if (typeof query === "object" && query !== null && query.type === "WITH" && canInlineSingleCte(query)) {
23127
23291
  const inlined = buildInlinedQuery(query);
23128
- const capability = await resolveSelectWhereCapability(inlined, tracedClient, cacheContext);
23292
+ const { resolver, rewrites } = await normalizeSelectChoiceEquality(
23293
+ inlined,
23294
+ tracedClient,
23295
+ cacheContext
23296
+ );
23297
+ if (rewrites.length > 0) explainChoiceEqualityRewrites.set(inlined, rewrites);
23298
+ const capability = classifyWhereCapability(inlined.where, resolver);
23129
23299
  const relativeNode = relativeNodeFor(inlined);
23130
23300
  if (capability.capability === "UNSUPPORTED" && !relativeNode) {
23131
23301
  throw new Error(`ArgumentError: WHERE predicate is unsupported (${formatWhereCapabilityFailure(capability)}).`);
@@ -23897,6 +24067,13 @@ function buildValidatePlan(stmt, label) {
23897
24067
  lines.push(" records/mutation API during EXPLAIN: none; violation count unavailable");
23898
24068
  return lines;
23899
24069
  }
24070
+ function formatChoiceEqualityRewrite(rewrite) {
24071
+ const field = rewrite.field.tableAlias ? `${rewrite.field.tableAlias}.${rewrite.field.field}` : rewrite.field.field;
24072
+ const originalValue = rewrite.value.replace(/'/g, "''");
24073
+ const normalizedValue = rewrite.value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
24074
+ const normalizedOperator = rewrite.normalizedOperator === "IN" ? "in" : "not in";
24075
+ return ` pushdown normalized: ${field} ${rewrite.originalOperator} '${originalValue}' -> ${field} ${normalizedOperator} ("${normalizedValue}")`;
24076
+ }
23900
24077
  function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlans, allowTotalCountPlan = true, emitFetch = true, collector = { sources: [] }, sourceRole = "main") {
23901
24078
  const whereCapability = capabilities?.get(stmt) ?? (capabilities ? [...capabilities].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
23902
24079
  const orderPlan = orderPlans?.get(stmt) ?? (orderPlans ? [...orderPlans].find(([candidate]) => JSON.stringify(candidate) === JSON.stringify(stmt))?.[1] : void 0);
@@ -23914,6 +24091,9 @@ function buildSelectPlan(stmt, label, capabilities, orderPlans, plainGroupByPlan
23914
24091
  );
23915
24092
  if (label) lines.push(label);
23916
24093
  lines.push(` mode: ${mode}`);
24094
+ for (const rewrite of explainChoiceEqualityRewrites.get(stmt) ?? choiceEqualityRewritesBySelect.get(stmt) ?? []) {
24095
+ lines.push(formatChoiceEqualityRewrite(rewrite));
24096
+ }
23917
24097
  if (groupingMetadata) {
23918
24098
  lines.push(` grouping source: ${groupingMetadata.source}`);
23919
24099
  lines.push(