@rex0220/kintone-sql-tools 3.29.0 → 3.30.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
@@ -5341,20 +5341,36 @@ function caseResultHasAggregate(result) {
5341
5341
  if (result.type === "ARRAY" || result.type === "FIELD_REF" || result.type === "ARITH") return false;
5342
5342
  return scalarValueHasAggregate(result);
5343
5343
  }
5344
- function collectRequiredFieldsByTable(stmt, plainGroupByPlan) {
5344
+ function collectSelectFieldReferencesBySource(stmt, plainGroupByPlan) {
5345
+ const unqualified = /* @__PURE__ */ new Set();
5346
+ const states = collectRequiredFieldsByTable(stmt, plainGroupByPlan, {
5347
+ includeMaterialized: true,
5348
+ unqualified
5349
+ });
5350
+ return {
5351
+ bySource: new Map(
5352
+ [...states.entries()].map(([table, state]) => [table, new Set(state.fields)])
5353
+ ),
5354
+ unqualified
5355
+ };
5356
+ }
5357
+ function collectRequiredFieldsByTable(stmt, plainGroupByPlan, sourceAware) {
5345
5358
  const allTables = [stmt.from, ...stmt.joins.map((j) => j.table)];
5346
5359
  const physicalTables = [stmt.from, ...stmt.joins.map((j) => j.table)].filter((t) => t.cteName === null);
5360
+ const targetTables = sourceAware ? allTables : physicalTables;
5347
5361
  const states = /* @__PURE__ */ new Map();
5348
- for (const table of physicalTables) {
5362
+ for (const table of targetTables) {
5349
5363
  states.set(table, { table, allFields: false, fields: /* @__PURE__ */ new Set() });
5350
5364
  }
5351
5365
  if (states.size === 0) return states;
5352
- const firstPhysicalTable = physicalTables[0] ?? null;
5353
- const subtableTable = physicalTables.find((t) => !!t.subtableCode) ?? null;
5366
+ const firstTargetTable = targetTables[0] ?? null;
5367
+ const subtableTable = targetTables.find((t) => !!t.subtableCode) ?? null;
5354
5368
  const aliasToTable = /* @__PURE__ */ new Map();
5355
- for (const table of physicalTables) {
5369
+ for (const table of targetTables) {
5356
5370
  if (table.alias) aliasToTable.set(table.alias, table);
5357
- if (!table.subtableCode) {
5371
+ if (table.cteName !== null) {
5372
+ aliasToTable.set(table.cteName, table);
5373
+ } else if (!table.subtableCode) {
5358
5374
  aliasToTable.set(`APP${table.appId}`, table);
5359
5375
  aliasToTable.set(`app${table.appId}`, table);
5360
5376
  }
@@ -5366,11 +5382,11 @@ function collectRequiredFieldsByTable(stmt, plainGroupByPlan) {
5366
5382
  st.allFields = true;
5367
5383
  st.fields.clear();
5368
5384
  };
5369
- const markAllPhysicalTables = () => {
5370
- for (const t of physicalTables) markAll(t);
5385
+ const markAllTargetTables = () => {
5386
+ for (const t of targetTables) markAll(t);
5371
5387
  };
5372
5388
  const markAllSubtableTables = () => {
5373
- for (const t of physicalTables) {
5389
+ for (const t of targetTables) {
5374
5390
  if (t.subtableCode) markAll(t);
5375
5391
  }
5376
5392
  };
@@ -5400,7 +5416,7 @@ function collectRequiredFieldsByTable(stmt, plainGroupByPlan) {
5400
5416
  if (rawName.endsWith(".*")) {
5401
5417
  const qualifier = rawName.slice(0, -2);
5402
5418
  if (!qualifier) {
5403
- markAllPhysicalTables();
5419
+ markAllTargetTables();
5404
5420
  return;
5405
5421
  }
5406
5422
  if (qualifier === "_p") {
@@ -5417,7 +5433,7 @@ function collectRequiredFieldsByTable(stmt, plainGroupByPlan) {
5417
5433
  if (phase === "groupBy" && groupResolution !== void 0) {
5418
5434
  if (groupResolution.kind === "PHYSICAL") {
5419
5435
  const source = allTables[groupResolution.sourceIndex];
5420
- if (source?.cteName === null) {
5436
+ if (source && (sourceAware || source.cteName === null)) {
5421
5437
  addFieldToTable(source, groupResolution.fieldCode);
5422
5438
  }
5423
5439
  }
@@ -5442,8 +5458,13 @@ function collectRequiredFieldsByTable(stmt, plainGroupByPlan) {
5442
5458
  addFieldToTable(target, field);
5443
5459
  return;
5444
5460
  }
5461
+ if (sourceAware) return;
5462
+ }
5463
+ if (sourceAware) {
5464
+ sourceAware.unqualified.add(rawName);
5465
+ } else if (firstTargetTable) {
5466
+ addFieldToTable(firstTargetTable, rawName);
5445
5467
  }
5446
- if (firstPhysicalTable) addFieldToTable(firstPhysicalTable, rawName);
5447
5468
  };
5448
5469
  const addFieldRef = (field, tableAlias, phase = "select") => {
5449
5470
  if (tableAlias) {
@@ -5456,6 +5477,7 @@ function collectRequiredFieldsByTable(stmt, plainGroupByPlan) {
5456
5477
  addFieldToTable(target, field);
5457
5478
  return;
5458
5479
  }
5480
+ if (sourceAware) return;
5459
5481
  }
5460
5482
  addFieldName(field, phase);
5461
5483
  };
@@ -5611,7 +5633,7 @@ function collectRequiredFieldsByTable(stmt, plainGroupByPlan) {
5611
5633
  for (const col of stmt.columns) {
5612
5634
  switch (col.type) {
5613
5635
  case "WILDCARD":
5614
- markAllPhysicalTables();
5636
+ markAllTargetTables();
5615
5637
  break;
5616
5638
  case "PARENT_WILDCARD":
5617
5639
  markAllSubtableTables();
@@ -9490,6 +9512,20 @@ function renderValidationValue(value) {
9490
9512
  }
9491
9513
 
9492
9514
  // src/core/existingRecordValidation.ts
9515
+ var VALIDATE_CONSTRAINT_CATEGORIES = [
9516
+ "required",
9517
+ "length",
9518
+ "range",
9519
+ "choice"
9520
+ ];
9521
+ function getAuditableConstraintCategories(field) {
9522
+ return [
9523
+ ...field.required === true ? ["required"] : [],
9524
+ ...field.minLength !== void 0 || field.maxLength !== void 0 ? ["length"] : [],
9525
+ ...field.minValue !== void 0 || field.maxValue !== void 0 ? ["range"] : [],
9526
+ ...field.optionOrder !== void 0 ? ["choice"] : []
9527
+ ];
9528
+ }
9493
9529
  function buildValidationFieldMetadataIndex(fieldInfos) {
9494
9530
  const topLevel = fieldInfos.filter((field) => !field.inSubtable);
9495
9531
  const childrenByTable = /* @__PURE__ */ new Map();
@@ -9506,7 +9542,7 @@ function buildValidationFieldMetadataIndex(fieldInfos) {
9506
9542
  };
9507
9543
  }
9508
9544
  function hasAuditableConstraint(field) {
9509
- return field.required === true || field.minValue !== void 0 || field.maxValue !== void 0 || field.minLength !== void 0 || field.maxLength !== void 0 || field.optionOrder !== void 0;
9545
+ return getAuditableConstraintCategories(field).length > 0;
9510
9546
  }
9511
9547
  function isExistingValidationAuditable(field) {
9512
9548
  return field.fieldType === "NUMBER" || hasAuditableConstraint(field);
@@ -15629,6 +15665,13 @@ async function executeExistingRecordValidationCore(stmt, client, options, cacheC
15629
15665
  const infoByCode = new Map(fieldInfos.filter((field) => !field.inSubtable).map((field) => [field.code, field]));
15630
15666
  const childCodes = new Set(fieldInfos.filter((field) => field.inSubtable).map((field) => field.code));
15631
15667
  const targets = resolveExistingValidationTargets(stmt, fieldInfos);
15668
+ const presentConstraintCategories = new Set(
15669
+ targets.flatMap((target) => getAuditableConstraintCategories(target.field))
15670
+ );
15671
+ const constraintMetadata = {
15672
+ present: VALIDATE_CONSTRAINT_CATEGORIES.filter((category) => presentConstraintCategories.has(category)),
15673
+ absent: VALIDATE_CONSTRAINT_CATEGORIES.filter((category) => !presentConstraintCategories.has(category))
15674
+ };
15632
15675
  const checkGroups = stmt.checkGroups ?? [];
15633
15676
  const checkRefs2 = collectCheckFieldRefs(checkGroups);
15634
15677
  for (const ref of checkRefs2) {
@@ -15785,7 +15828,11 @@ async function executeExistingRecordValidationCore(stmt, client, options, cacheC
15785
15828
  columns: [...columns],
15786
15829
  rows,
15787
15830
  rowCount: rows.length,
15788
- validateStats: { errorRecords: errorRecordIds.size, errorCount }
15831
+ validateStats: {
15832
+ errorRecords: errorRecordIds.size,
15833
+ errorCount,
15834
+ constraintMetadata
15835
+ }
15789
15836
  };
15790
15837
  materializedMetaBySelectResult.set(result, existingValidationColumnMeta(stmt.summary === true));
15791
15838
  return result;
@@ -17093,6 +17140,159 @@ async function validateSelectFieldCodes(stmt, mode, client, cacheContext) {
17093
17140
  }
17094
17141
  }
17095
17142
  }
17143
+ function b86SourceLabel(table) {
17144
+ return table.cteName ?? `APP${table.appId}`;
17145
+ }
17146
+ function b86SourceAliases(table) {
17147
+ if (table.alias) return [table.alias];
17148
+ if (table.cteName !== null) return [table.cteName];
17149
+ return [`APP${table.appId}`, `app${table.appId}`];
17150
+ }
17151
+ function b86PhysicalFieldExists(schema, field) {
17152
+ return isSystemLikeFieldCode(field) || schema.validCodes.has(field);
17153
+ }
17154
+ function b86FieldExists(schema, field) {
17155
+ if (schema.table.cteName !== null) return schema.validCodes.has(field);
17156
+ return b86PhysicalFieldExists(schema, field);
17157
+ }
17158
+ function collectB86Subqueries(stmt) {
17159
+ const queries = [];
17160
+ const visitWhere = (where) => {
17161
+ if (where === null) return;
17162
+ switch (where.type) {
17163
+ case "BINARY":
17164
+ if (where.right.type === "SUBQUERY_IN_LIST" || where.right.type === "SCALAR_SUBQUERY") {
17165
+ queries.push(where.right.query);
17166
+ }
17167
+ return;
17168
+ case "EXISTS":
17169
+ queries.push(where.query);
17170
+ return;
17171
+ case "LOGICAL":
17172
+ visitWhere(where.left);
17173
+ visitWhere(where.right);
17174
+ return;
17175
+ case "NOT":
17176
+ case "GROUP":
17177
+ visitWhere(where.expr);
17178
+ return;
17179
+ case "NULL_CHECK":
17180
+ case "BOOLEAN":
17181
+ return;
17182
+ }
17183
+ };
17184
+ visitWhere(stmt.where);
17185
+ visitWhere(stmt.having);
17186
+ for (const column of stmt.columns) {
17187
+ if (column.type === "SCALAR_SUBQUERY_COL") {
17188
+ queries.push(column.query);
17189
+ } else if (column.type === "CASE_COL") {
17190
+ for (const branch of column.expr.branches) visitWhere(branch.condition);
17191
+ }
17192
+ }
17193
+ return queries;
17194
+ }
17195
+ async function validateB86SelectFieldCodes(stmt, client, cteCache, cacheContext) {
17196
+ const tables = [stmt.from, ...stmt.joins.map((join2) => join2.table)];
17197
+ const materializedByTable = /* @__PURE__ */ new Map();
17198
+ const effectiveAliases = /* @__PURE__ */ new Set();
17199
+ for (const table of tables) {
17200
+ const alias = effectiveTableAlias(table);
17201
+ if (alias === null) continue;
17202
+ if (effectiveAliases.has(alias)) {
17203
+ throw new Error(`ArgumentError: effective alias ${alias} is used by multiple tables.`);
17204
+ }
17205
+ effectiveAliases.add(alias);
17206
+ }
17207
+ for (const table of tables) {
17208
+ if (table.cteName === null || table.cteName === NO_FROM_CTE_NAME) continue;
17209
+ const materialized = cteCache.get(table.cteName);
17210
+ if (!materialized) {
17211
+ throw new Error(`ArgumentError: materialized source ${table.cteName} is not available.`);
17212
+ }
17213
+ if (materialized.rows.length > 0 && materialized.columns.length === 0) {
17214
+ throw new Error(
17215
+ `InternalError: materialized source ${table.cteName} has rows but no column schema.`
17216
+ );
17217
+ }
17218
+ if (stmt.joins.length > 0 && materialized.rows.length === 0 && materialized.columns.length === 0) {
17219
+ throw new Error(
17220
+ `ArgumentError: column schema is unavailable for materialized JOIN source ${table.cteName}.`
17221
+ );
17222
+ }
17223
+ materializedByTable.set(table, materialized);
17224
+ }
17225
+ const schemas = /* @__PURE__ */ new Map();
17226
+ await Promise.all(tables.map(async (table) => {
17227
+ const materialized = materializedByTable.get(table);
17228
+ if (materialized) {
17229
+ schemas.set(table, {
17230
+ table,
17231
+ label: b86SourceLabel(table),
17232
+ validCodes: new Set(materialized.columns),
17233
+ authoritative: true,
17234
+ schemaUnavailable: materialized.rows.length === 0 && materialized.columns.length === 0
17235
+ });
17236
+ return;
17237
+ }
17238
+ if (table.cteName === NO_FROM_CTE_NAME) return;
17239
+ const defs = await getFieldsCached(table.appId, client, cacheContext);
17240
+ schemas.set(table, {
17241
+ table,
17242
+ label: b86SourceLabel(table),
17243
+ validCodes: new Set(defs.map((def) => def.code)),
17244
+ authoritative: defs.length > 0,
17245
+ schemaUnavailable: false
17246
+ });
17247
+ }));
17248
+ const sourceByAlias = /* @__PURE__ */ new Map();
17249
+ for (const schema of schemas.values()) {
17250
+ for (const alias of b86SourceAliases(schema.table)) sourceByAlias.set(alias, schema);
17251
+ }
17252
+ for (const join2 of stmt.joins) {
17253
+ for (const ref of [join2.on.left, join2.on.right]) {
17254
+ if (!ref.tableAlias) continue;
17255
+ const schema = sourceByAlias.get(ref.tableAlias);
17256
+ if (schema && schema.table.cteName !== null && !schema.schemaUnavailable && !b86FieldExists(schema, ref.field)) {
17257
+ throw new Error(
17258
+ `ArgumentError: JOIN key ${ref.tableAlias}.${ref.field} is not available in the materialized table.`
17259
+ );
17260
+ }
17261
+ }
17262
+ }
17263
+ const references = collectSelectFieldReferencesBySource(stmt);
17264
+ for (const [table, fields] of references.bySource) {
17265
+ const schema = schemas.get(table);
17266
+ if (!schema || schema.schemaUnavailable || !schema.authoritative) continue;
17267
+ const unknown = [...fields].filter((field) => !b86FieldExists(schema, field));
17268
+ if (unknown.length > 0) {
17269
+ throw new Error(
17270
+ `ArgumentError: unknown field code(s): ${unknown.join(", ")} (${schema.label})`
17271
+ );
17272
+ }
17273
+ }
17274
+ for (const field of references.unqualified) {
17275
+ const candidates = [...schemas.values()].filter((schema) => !schema.schemaUnavailable);
17276
+ if (candidates.some((schema) => b86FieldExists(schema, field))) continue;
17277
+ if ([...schemas.values()].some((schema) => schema.schemaUnavailable)) continue;
17278
+ if (candidates.some((schema) => schema.table.cteName === null && !schema.authoritative)) continue;
17279
+ const labels = candidates.map((schema) => schema.label).join(", ");
17280
+ throw new Error(`ArgumentError: unknown field code(s): ${field} (${labels})`);
17281
+ }
17282
+ }
17283
+ async function preflightB86QueryWithCte(query, client, cteCache, cacheContext, seen = /* @__PURE__ */ new Set()) {
17284
+ if (seen.has(query)) return;
17285
+ seen.add(query);
17286
+ if (query.type === "UNION") {
17287
+ await preflightB86QueryWithCte(query.left, client, cteCache, cacheContext, seen);
17288
+ await preflightB86QueryWithCte(query.right, client, cteCache, cacheContext, seen);
17289
+ return;
17290
+ }
17291
+ await validateB86SelectFieldCodes(query, client, cteCache, cacheContext);
17292
+ for (const subquery of collectB86Subqueries(query)) {
17293
+ await preflightB86QueryWithCte(subquery, client, cteCache, cacheContext, seen);
17294
+ }
17295
+ }
17096
17296
  function extractMainTypedPushdownCandidate(stmt) {
17097
17297
  if (stmt.where === null || stmt.from.subtableCode || stmt.from.cteName !== null) return null;
17098
17298
  if (stmt.joins.length === 0) {
@@ -17954,11 +18154,14 @@ async function executeWith(stmt, client, options, cacheContext, seed, captureCol
17954
18154
  }
17955
18155
  return executeQueryWithCte(stmt.query, client, options, cteCache, cacheContext, captureColumnMeta);
17956
18156
  }
17957
- async function executeQueryWithCte(query, client, options, cteCache, cacheContext, captureColumnMeta = false) {
18157
+ async function executeQueryWithCte(query, client, options, cteCache, cacheContext, captureColumnMeta = false, b86PreflightComplete = false) {
18158
+ if (!b86PreflightComplete) {
18159
+ await preflightB86QueryWithCte(query, client, cteCache, cacheContext);
18160
+ }
17958
18161
  if (query.type === "UNION") {
17959
18162
  const [leftResult, rightResult] = await Promise.all([
17960
- executeQueryWithCte(query.left, client, options, cteCache, cacheContext, captureColumnMeta),
17961
- executeQueryWithCte(query.right, client, options, cteCache, cacheContext, captureColumnMeta)
18163
+ executeQueryWithCte(query.left, client, options, cteCache, cacheContext, captureColumnMeta, true),
18164
+ executeQueryWithCte(query.right, client, options, cteCache, cacheContext, captureColumnMeta, true)
17962
18165
  ]);
17963
18166
  const leftCols = leftResult.columns;
17964
18167
  const rightCols = rightResult.columns;