@duckcodeailabs/dql-cli 1.10.3 → 1.10.4

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.
Files changed (32) hide show
  1. package/dist/assets/dql-notebook/assets/{index-VvEaTurJ.js → index-m7ziVzoC.js} +132 -132
  2. package/dist/assets/dql-notebook/index.html +1 -1
  3. package/dist/commands/doctor.d.ts +1 -0
  4. package/dist/commands/doctor.d.ts.map +1 -1
  5. package/dist/commands/doctor.js +15 -0
  6. package/dist/commands/doctor.js.map +1 -1
  7. package/dist/local-runtime.d.ts +8 -0
  8. package/dist/local-runtime.d.ts.map +1 -1
  9. package/dist/local-runtime.js +307 -140
  10. package/dist/local-runtime.js.map +1 -1
  11. package/dist/package.json +10 -10
  12. package/dist/semantic-execution/connection-identity.d.ts +11 -0
  13. package/dist/semantic-execution/connection-identity.d.ts.map +1 -0
  14. package/dist/semantic-execution/connection-identity.js +81 -0
  15. package/dist/semantic-execution/connection-identity.js.map +1 -0
  16. package/dist/semantic-execution/execution-gateway.d.ts +42 -0
  17. package/dist/semantic-execution/execution-gateway.d.ts.map +1 -0
  18. package/dist/semantic-execution/execution-gateway.js +203 -0
  19. package/dist/semantic-execution/execution-gateway.js.map +1 -0
  20. package/dist/semantic-execution/target-binding.d.ts +44 -0
  21. package/dist/semantic-execution/target-binding.d.ts.map +1 -0
  22. package/dist/semantic-execution/target-binding.js +185 -0
  23. package/dist/semantic-execution/target-binding.js.map +1 -0
  24. package/dist/semantic-runtime-settings.d.ts +7 -1
  25. package/dist/semantic-runtime-settings.d.ts.map +1 -1
  26. package/dist/semantic-runtime-settings.js +8 -1
  27. package/dist/semantic-runtime-settings.js.map +1 -1
  28. package/dist/semantic-runtime.d.ts +6 -2
  29. package/dist/semantic-runtime.d.ts.map +1 -1
  30. package/dist/semantic-runtime.js +13 -5
  31. package/dist/semantic-runtime.js.map +1 -1
  32. package/package.json +10 -10
@@ -35,6 +35,10 @@ import { MetricFlowUnavailableError, hasDbtSemanticManifest, } from "./metricflo
35
35
  import { ManagedMetricFlowInstaller, isMetricFlowWarehouseAdapter, metricFlowAdapterForDriver, } from './metricflow-installer.js';
36
36
  import { applySemanticPathSelection, compileSemanticRuntimeQuery, decodeSemanticPathEvidenceId, describeRuntimeCompatibility, explainMissingSemanticRuntime, getSemanticRuntimeStatus, semanticRuntimeErrorDetails, semanticRuntimeErrorCode, semanticMetricExecutionCapability as runtimeMetricExecutionCapability, SemanticRuntimeRequiredError, testSemanticRuntimeDraft, parseSemanticDimensionSelection, } from './semantic-runtime.js';
37
37
  import { getSemanticRuntimeSettings, saveTestedSemanticRuntimeSettings, saveSemanticRuntimePreference, } from './semantic-runtime-settings.js';
38
+ import { observeWarehouseTargetIdentity } from './semantic-execution/connection-identity.js';
39
+ import { configuredWarehouseTargetIdentity } from './semantic-execution/connection-identity.js';
40
+ import { executeTargetBoundSemanticQuery, semanticExecutionFailureCode, semanticExecutionFailureDetails, } from './semantic-execution/execution-gateway.js';
41
+ import { buildSemanticTargetBinding } from './semantic-execution/target-binding.js';
38
42
  import { NotebookDatasetWorkspace, } from "./notebook-datasets.js";
39
43
  import { prepareBlockInvocation } from './block-invocation.js';
40
44
  const NOTEBOOK_EXECUTE_PREVIEW_ROW_LIMIT = 500;
@@ -585,6 +589,10 @@ export async function startLocalServer(opts) {
585
589
  : null;
586
590
  let connection = configuredConnection ?? resolveDbtProfileRuntimeConnection(projectRoot, projectConfig);
587
591
  const projectSnapshots = new ProjectSnapshotService();
592
+ // Server-lifetime registry for exact compiler-owned SQL. The downstream DQL
593
+ // artifact executor uses this to preserve the selected adapter and target
594
+ // rather than treating compiled semantic SQL as unrelated generic SQL.
595
+ const compiledSemanticQueries = new Map();
588
596
  const dashboardRunEvidence = new Map();
589
597
  const dbtNodeDetailCache = new Map();
590
598
  const onboardingJobs = new Map();
@@ -1282,30 +1290,42 @@ export async function startLocalServer(opts) {
1282
1290
  }
1283
1291
  projectSnapshot();
1284
1292
  projectSnapshots.assertCurrent(runProjectSnapshot.snapshotId);
1285
- const compiled = await compileSemanticRuntimeQuery({
1286
- metrics: [binding.metric],
1287
- dimensions: [],
1288
- timeDimension: {
1289
- name: binding.timeDimension,
1290
- granularity: binding.granularity,
1291
- },
1292
- orderBy: [{ name: binding.outputField, direction: 'desc' }],
1293
- limit: 1,
1294
- }, {
1293
+ const plannedAdapter = await resolvePlannedSemanticAdapter(projectRoot, undefined);
1294
+ const execution = await executeTargetBoundSemanticQuery({
1295
+ executor,
1296
+ connection: semanticConnection,
1295
1297
  projectRoot,
1296
- projectConfig,
1297
- detectedProvider: semanticDetectedProvider,
1298
- semanticLayer: semanticLayer,
1299
- driver: semanticDriver,
1300
- tableMapping: semanticTableMapping,
1298
+ plannedAdapter,
1299
+ metricFlow: plannedAdapter === 'metricflow-cli'
1300
+ ? resolveMetricFlowTargetMetadata(projectRoot, projectConfig)
1301
+ : undefined,
1302
+ compile: () => compileSemanticRuntimeQuery({
1303
+ metrics: [binding.metric],
1304
+ dimensions: [],
1305
+ timeDimension: {
1306
+ name: binding.timeDimension,
1307
+ granularity: binding.granularity,
1308
+ },
1309
+ orderBy: [{ name: binding.outputField, direction: 'desc' }],
1310
+ limit: 1,
1311
+ engine: plannedAdapter,
1312
+ }, {
1313
+ projectRoot,
1314
+ projectConfig,
1315
+ detectedProvider: semanticDetectedProvider,
1316
+ semanticLayer: semanticLayer,
1317
+ driver: semanticDriver,
1318
+ tableMapping: semanticTableMapping,
1319
+ }),
1320
+ prepareSql: (sql) => prepareLocalExecution(sql, semanticConnection, projectRoot, projectConfig),
1321
+ rowBound: 1,
1301
1322
  });
1302
- if (!compiled) {
1323
+ if (!execution) {
1303
1324
  throw Object.assign(new Error('The authorized semantic freshness query could not be compiled.'), {
1304
1325
  code: 'COMPILATION_FAILED',
1305
1326
  });
1306
1327
  }
1307
- const prepared = prepareLocalExecution(compiled.sql, semanticConnection, projectRoot, projectConfig);
1308
- const result = await executor.executeQuery(prepared.sql, [], runtimeVariables({}), prepared.connection);
1328
+ const result = execution.result;
1309
1329
  projectSnapshot();
1310
1330
  projectSnapshots.assertCurrent(runProjectSnapshot.snapshotId);
1311
1331
  return {
@@ -1325,9 +1345,27 @@ export async function startLocalServer(opts) {
1325
1345
  ...selection,
1326
1346
  dimensions: selection.dimensions ?? [],
1327
1347
  };
1328
- const compiled = await compileSemanticRuntimeQuery(requestedPath
1348
+ const requestedSemanticQuery = requestedPath
1329
1349
  ? applySemanticPathSelection(semanticRequest, requestedPath.authoringReference, requestedPath.entityPath)
1330
- : semanticRequest, {
1350
+ : semanticRequest;
1351
+ const plannedAdapter = await resolvePlannedSemanticAdapter(projectRoot, requestedSemanticQuery.engine);
1352
+ const executionTarget = semanticConnection
1353
+ ? await observeWarehouseTargetIdentity(executor, semanticConnection)
1354
+ : undefined;
1355
+ const targetBinding = executionTarget
1356
+ ? buildSemanticTargetBinding({
1357
+ projectRoot,
1358
+ adapterId: plannedAdapter,
1359
+ executionTarget,
1360
+ metricFlow: plannedAdapter === 'metricflow-cli'
1361
+ ? resolveMetricFlowTargetMetadata(projectRoot, projectConfig)
1362
+ : undefined,
1363
+ })
1364
+ : undefined;
1365
+ const compiled = await compileSemanticRuntimeQuery({
1366
+ ...requestedSemanticQuery,
1367
+ engine: plannedAdapter,
1368
+ }, {
1331
1369
  projectRoot,
1332
1370
  projectConfig,
1333
1371
  detectedProvider: semanticDetectedProvider,
@@ -1341,6 +1379,7 @@ export async function startLocalServer(opts) {
1341
1379
  : projectConfig.dbt?.projectDir;
1342
1380
  throw new SemanticRuntimeRequiredError(await explainMissingSemanticRuntime(projectRoot, dbtProjectPath));
1343
1381
  }
1382
+ compiledSemanticQueries.set(executionFingerprint(compiled.sql), compiled);
1344
1383
  return {
1345
1384
  sql: compiled.sql,
1346
1385
  engine: compiled.engine,
@@ -1352,7 +1391,10 @@ export async function startLocalServer(opts) {
1352
1391
  ? { timeDimension: compiled.effectiveRequest.timeDimension }
1353
1392
  : {}),
1354
1393
  },
1355
- trace: compiled.semanticTrace,
1394
+ trace: {
1395
+ ...compiled.semanticTrace,
1396
+ ...(targetBinding ? { targetBinding } : {}),
1397
+ },
1356
1398
  };
1357
1399
  },
1358
1400
  } : {}),
@@ -2778,7 +2820,10 @@ export async function startLocalServer(opts) {
2778
2820
  throw new Error(`I need values for: ${invocation.unresolvedParameters.join(', ')}.`);
2779
2821
  }
2780
2822
  const activeConnection = requireActiveConnection();
2781
- const tableMapping = await resolveSemanticTableMapping(executor, activeConnection, semanticLayer);
2823
+ const precompiledSemanticQuery = Boolean(extractBlockStudioSql(source));
2824
+ const tableMapping = !precompiledSemanticQuery
2825
+ ? await resolveSemanticTableMapping(executor, activeConnection, semanticLayer)
2826
+ : undefined;
2782
2827
  const semanticCompose = semanticLayer
2783
2828
  ? await composeSemanticBlockSqlForRuntime(source, semanticLayer, {
2784
2829
  driver: activeConnection.driver,
@@ -2807,7 +2852,24 @@ export async function startLocalServer(opts) {
2807
2852
  const app = loadRuntimeApp(projectRoot, activePersonaAppId());
2808
2853
  const sourceDomain = metadata.domain ?? source.match(/\bdomain\s*=\s*"([^"]+)"/i)?.[1];
2809
2854
  assertAppAccess({ app, domain: sourceDomain ?? app?.domain, level: 'execute' });
2810
- const rawResult = await executor.executeQuery(executionSql, plan?.sqlParams ?? [], runtimeVariables({ ...(plan?.variables ?? {}), ...invocation.values }), prepared.connection);
2855
+ const pinnedSemanticCompile = semanticCompose?.sql
2856
+ ? compiledSemanticQueries.get(executionFingerprint(semanticCompose.sql))
2857
+ : undefined;
2858
+ const semanticExecution = pinnedSemanticCompile
2859
+ ? await executeTargetBoundSemanticQuery({
2860
+ executor,
2861
+ connection: activeConnection,
2862
+ projectRoot,
2863
+ plannedAdapter: pinnedSemanticCompile.engine,
2864
+ metricFlow: pinnedSemanticCompile.engine === 'metricflow-cli'
2865
+ ? resolveMetricFlowTargetMetadata(projectRoot, projectConfig)
2866
+ : undefined,
2867
+ compile: async () => pinnedSemanticCompile,
2868
+ prepareSql: () => ({ sql: executionSql, connection: prepared.connection }),
2869
+ rowBound: invocationInput?.rowLimit ?? pinnedSemanticCompile.effectiveRequest.limit,
2870
+ })
2871
+ : null;
2872
+ const rawResult = semanticExecution?.result ?? await executor.executeQuery(executionSql, plan?.sqlParams ?? [], runtimeVariables({ ...(plan?.variables ?? {}), ...invocation.values }), prepared.connection);
2811
2873
  const normalized = normalizeQueryResult(rawResult);
2812
2874
  const executionReceipt = createDqlArtifactExecutionReceipt(source, executionSql, invocation.values, normalized);
2813
2875
  return {
@@ -2822,6 +2884,11 @@ export async function startLocalServer(opts) {
2822
2884
  parameters: invocation.resolvedParameters,
2823
2885
  auditId: invocation.auditId,
2824
2886
  executionReceipt,
2887
+ ...(semanticExecution ? {
2888
+ semanticExecutionReceipt: semanticExecution.executionReceipt,
2889
+ semanticTargetBinding: semanticExecution.targetBinding,
2890
+ semanticTrace: semanticExecution.semanticTrace,
2891
+ } : {}),
2825
2892
  };
2826
2893
  };
2827
2894
  const executeCertifiedBlockByNameForAgent = async (blockName, invocationInput, requireCertified = false) => {
@@ -3010,14 +3077,10 @@ export async function startLocalServer(opts) {
3010
3077
  const scanRuntimeSchema = async () => {
3011
3078
  if (!connection)
3012
3079
  return { ranked: [], snapshot: [] };
3013
- const result = await executor.executeQuery(`SELECT table_schema, table_name, column_name, data_type
3014
- FROM information_schema.columns
3015
- WHERE UPPER(table_schema) NOT IN ('INFORMATION_SCHEMA', 'PG_CATALOG')
3016
- ORDER BY table_schema, table_name, ordinal_position
3017
- LIMIT 2000`, [], runtimeVariables({}), connection);
3080
+ const result = await executor.executePositional(buildRuntimeSchemaSearchSql(question), [], connection, { maxRows: 600, maxBytes: 2 * 1024 * 1024, batchSize: 200, deadlineMs: 30_000 });
3018
3081
  return {
3019
3082
  ranked: buildAgentSchemaContext(question, result.rows),
3020
- snapshot: buildAgentSchemaContext(question, result.rows, { includeUnscored: true, limit: 500 }),
3083
+ snapshot: buildAgentSchemaContext(question, result.rows, { includeUnscored: true, limit: 100 }),
3021
3084
  };
3022
3085
  };
3023
3086
  const catalogContext = await buildAgentSchemaContextFromCatalog(projectRoot, question, preparedContextPack).catch(() => []);
@@ -3039,7 +3102,7 @@ export async function startLocalServer(opts) {
3039
3102
  const enriched = valueGrounding.mode === 'safe_automatic'
3040
3103
  ? await enrichAgentSchemaContextWithValueMatches(question, schemaContext.ranked, executor, connection, valueGrounding.searchSafeColumns)
3041
3104
  : schemaContext.ranked;
3042
- recordAgentRuntimeSchemaSnapshot(projectRoot, schemaContext.snapshot, 'full information_schema runtime scan');
3105
+ recordAgentRuntimeSchemaSnapshot(projectRoot, schemaContext.snapshot, 'question-scoped information_schema search');
3043
3106
  return enriched;
3044
3107
  }
3045
3108
  catch {
@@ -7443,8 +7506,15 @@ export async function startLocalServer(opts) {
7443
7506
  refreshedAt: dataset.refreshedAt,
7444
7507
  trustState: dataset.trustState,
7445
7508
  }));
7509
+ const schemaLimit = Math.min(500, Math.max(1, Number(url.searchParams.get('limit')) || 200));
7510
+ const schemaOffset = Math.max(0, Number(url.searchParams.get('offset')) || 0);
7511
+ const schemaSearch = (url.searchParams.get('q') ?? '').trim();
7446
7512
  const { tables, columnsByPath } = connection
7447
- ? await introspectSchema(executor, connection)
7513
+ ? await introspectSchema(executor, connection, {
7514
+ limit: schemaLimit,
7515
+ offset: schemaOffset,
7516
+ search: schemaSearch,
7517
+ })
7448
7518
  : { tables: [], columnsByPath: new Map() };
7449
7519
  const dbTables = tables.map((t) => ({
7450
7520
  name: t.path,
@@ -9342,7 +9412,9 @@ export async function startLocalServer(opts) {
9342
9412
  try {
9343
9413
  const body = await readJSON(req);
9344
9414
  const result = await testSemanticRuntimeDraft(projectRoot, body);
9345
- const settings = saveTestedSemanticRuntimeSettings(projectRoot, body, result);
9415
+ const targetConnection = requireActiveConnection(connection);
9416
+ const executionTarget = await observeWarehouseTargetIdentity(executor, targetConnection);
9417
+ const settings = saveTestedSemanticRuntimeSettings(projectRoot, body, result, executionTarget);
9346
9418
  const runtime = await getSemanticRuntimeStatus(projectRoot);
9347
9419
  res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
9348
9420
  res.end(serializeJSON({ ok: true, ...settings, runtime }));
@@ -10411,25 +10483,41 @@ export async function startLocalServer(opts) {
10411
10483
  const { metrics = [], dimensions = [], filters = [], limit, timeDimension, orderBy, savedQuery, engine } = body;
10412
10484
  // Resolve which connection to use — request can override default
10413
10485
  const targetConnection = requireActiveConnection(isConnectionConfig(body.connection) ? body.connection : connection);
10414
- const driver = targetConnection.driver;
10415
- const tableMapping = await resolveSemanticTableMapping(executor, targetConnection, semanticLayer);
10416
- const composed = await composeRuntimeSemanticQuery({
10417
- metrics,
10418
- dimensions,
10419
- filters,
10420
- limit,
10421
- timeDimension,
10422
- orderBy,
10423
- savedQuery,
10424
- engine,
10425
- }, semanticLayer, {
10486
+ const plannedAdapter = await resolvePlannedSemanticAdapter(projectRoot, engine);
10487
+ const metricFlow = plannedAdapter === 'metricflow-cli'
10488
+ ? resolveMetricFlowTargetMetadata(projectRoot, projectConfig)
10489
+ : undefined;
10490
+ const execution = await executeTargetBoundSemanticQuery({
10491
+ executor,
10492
+ connection: targetConnection,
10426
10493
  projectRoot,
10427
- projectConfig,
10428
- detectedProvider: semanticDetectedProvider,
10429
- driver,
10430
- tableMapping,
10494
+ plannedAdapter,
10495
+ metricFlow,
10496
+ compile: async () => {
10497
+ const tableMapping = plannedAdapter === 'native'
10498
+ ? await resolveSemanticTableMapping(executor, targetConnection, semanticLayer)
10499
+ : undefined;
10500
+ return composeRuntimeSemanticQuery({
10501
+ metrics,
10502
+ dimensions,
10503
+ filters,
10504
+ limit,
10505
+ timeDimension,
10506
+ orderBy,
10507
+ savedQuery,
10508
+ engine: plannedAdapter,
10509
+ }, semanticLayer, {
10510
+ projectRoot,
10511
+ projectConfig,
10512
+ detectedProvider: semanticDetectedProvider,
10513
+ driver: targetConnection.driver,
10514
+ tableMapping,
10515
+ });
10516
+ },
10517
+ prepareSql: (sql) => prepareLocalExecution(sql, targetConnection, projectRoot, projectConfig),
10518
+ rowBound: limit,
10431
10519
  });
10432
- if (!composed) {
10520
+ if (!execution) {
10433
10521
  const provider = semanticConfig?.provider ?? semanticDetectedProvider ?? 'dql';
10434
10522
  const semanticRuntime = await getSemanticRuntimeStatus(projectRoot, { probeConfiguredCloud: true });
10435
10523
  const blocked = metrics.map((metricName) => ({
@@ -10446,20 +10534,20 @@ export async function startLocalServer(opts) {
10446
10534
  }));
10447
10535
  return;
10448
10536
  }
10449
- // Execute the composed SQL against the resolved connection
10450
- const prepared = prepareLocalExecution(composed.sql, targetConnection, projectRoot, projectConfig);
10451
- const result = await executor.executeQuery(prepared.sql, [], {}, prepared.connection);
10452
10537
  res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
10453
10538
  res.end(serializeJSON({
10454
- sql: composed.sql,
10455
- tables: composed.tables,
10456
- joins: composed.joins,
10457
- engine: composed.engine,
10458
- effectiveRequest: composed.effectiveRequest,
10459
- runtimeRequest: composed.runtimeRequest,
10460
- semanticTrace: composed.semanticTrace,
10461
- warnings: composed.warnings,
10462
- result: normalizeQueryResult(result),
10539
+ sql: execution.compiled.sql,
10540
+ executedSql: execution.preparedSql,
10541
+ tables: execution.compiled.tables,
10542
+ joins: execution.compiled.joins,
10543
+ engine: execution.compiled.engine,
10544
+ effectiveRequest: execution.compiled.effectiveRequest,
10545
+ runtimeRequest: execution.compiled.runtimeRequest,
10546
+ semanticTrace: execution.semanticTrace,
10547
+ warnings: execution.compiled.warnings,
10548
+ targetBinding: execution.targetBinding,
10549
+ executionReceipt: execution.executionReceipt,
10550
+ result: normalizeQueryResult(execution.result),
10463
10551
  }));
10464
10552
  }
10465
10553
  catch (error) {
@@ -10468,13 +10556,13 @@ export async function startLocalServer(opts) {
10468
10556
  res.end(serializeJSON({ error: error.message, code: 'unauthorized' }));
10469
10557
  return;
10470
10558
  }
10471
- const runtimeCode = semanticRuntimeErrorCode(error);
10472
- const status = runtimeCode ? 400 : 500;
10559
+ const runtimeCode = semanticExecutionFailureCode(error) ?? semanticRuntimeErrorCode(error);
10560
+ const status = runtimeCode === 'EXECUTION_TARGET_MISMATCH' ? 409 : runtimeCode ? 400 : 500;
10473
10561
  res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
10474
10562
  res.end(serializeJSON({
10475
10563
  error: error instanceof Error ? error.message : String(error),
10476
10564
  code: runtimeCode,
10477
- details: semanticRuntimeErrorDetails(error),
10565
+ details: semanticExecutionFailureDetails(error) ?? semanticRuntimeErrorDetails(error),
10478
10566
  hint: runtimeCode === 'SEMANTIC_RUNTIME_REQUIRED'
10479
10567
  ? 'Configure dbt Cloud Semantic Layer in Project & dbt settings, or install a compatible local MetricFlow runtime.'
10480
10568
  : undefined,
@@ -10492,25 +10580,41 @@ export async function startLocalServer(opts) {
10492
10580
  const body = await readJSON(req);
10493
10581
  const { metrics = [], dimensions = [], filters = [], limit, timeDimension, orderBy, savedQuery, engine } = body;
10494
10582
  const targetConnection = requireActiveConnection(isConnectionConfig(body.connection) ? body.connection : connection);
10495
- const driver = targetConnection.driver;
10496
- const tableMapping = await resolveSemanticTableMapping(executor, targetConnection, semanticLayer);
10497
- const composed = await composeRuntimeSemanticQuery({
10498
- metrics,
10499
- dimensions,
10500
- filters,
10501
- limit,
10502
- timeDimension,
10503
- orderBy,
10504
- savedQuery,
10505
- engine,
10506
- }, semanticLayer, {
10583
+ const plannedAdapter = await resolvePlannedSemanticAdapter(projectRoot, engine);
10584
+ const metricFlow = plannedAdapter === 'metricflow-cli'
10585
+ ? resolveMetricFlowTargetMetadata(projectRoot, projectConfig)
10586
+ : undefined;
10587
+ const execution = await executeTargetBoundSemanticQuery({
10588
+ executor,
10589
+ connection: targetConnection,
10507
10590
  projectRoot,
10508
- projectConfig,
10509
- detectedProvider: semanticDetectedProvider,
10510
- driver,
10511
- tableMapping,
10591
+ plannedAdapter,
10592
+ metricFlow,
10593
+ compile: async () => {
10594
+ const tableMapping = plannedAdapter === 'native'
10595
+ ? await resolveSemanticTableMapping(executor, targetConnection, semanticLayer)
10596
+ : undefined;
10597
+ return composeRuntimeSemanticQuery({
10598
+ metrics,
10599
+ dimensions,
10600
+ filters,
10601
+ limit,
10602
+ timeDimension,
10603
+ orderBy,
10604
+ savedQuery,
10605
+ engine: plannedAdapter,
10606
+ }, semanticLayer, {
10607
+ projectRoot,
10608
+ projectConfig,
10609
+ detectedProvider: semanticDetectedProvider,
10610
+ driver: targetConnection.driver,
10611
+ tableMapping,
10612
+ });
10613
+ },
10614
+ prepareSql: (sql) => prepareLocalExecution(sql, targetConnection, projectRoot, projectConfig),
10615
+ rowBound: limit,
10512
10616
  });
10513
- if (!composed) {
10617
+ if (!execution) {
10514
10618
  const provider = semanticConfig?.provider ?? semanticDetectedProvider ?? 'dql';
10515
10619
  const semanticRuntime = await getSemanticRuntimeStatus(projectRoot, { probeConfiguredCloud: true });
10516
10620
  const blocked = metrics.map((metricName) => ({
@@ -10527,29 +10631,30 @@ export async function startLocalServer(opts) {
10527
10631
  }));
10528
10632
  return;
10529
10633
  }
10530
- const prepared = prepareLocalExecution(composed.sql, targetConnection, projectRoot, projectConfig);
10531
- const result = await executor.executeQuery(prepared.sql, [], {}, prepared.connection);
10532
10634
  res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
10533
10635
  res.end(serializeJSON({
10534
- sql: composed.sql,
10535
- joins: composed.joins,
10536
- tables: composed.tables,
10537
- engine: composed.engine,
10538
- effectiveRequest: composed.effectiveRequest,
10539
- runtimeRequest: composed.runtimeRequest,
10540
- semanticTrace: composed.semanticTrace,
10541
- warnings: composed.warnings,
10542
- result: normalizeQueryResult(result),
10636
+ sql: execution.compiled.sql,
10637
+ executedSql: execution.preparedSql,
10638
+ joins: execution.compiled.joins,
10639
+ tables: execution.compiled.tables,
10640
+ engine: execution.compiled.engine,
10641
+ effectiveRequest: execution.compiled.effectiveRequest,
10642
+ runtimeRequest: execution.compiled.runtimeRequest,
10643
+ semanticTrace: execution.semanticTrace,
10644
+ warnings: execution.compiled.warnings,
10645
+ targetBinding: execution.targetBinding,
10646
+ executionReceipt: execution.executionReceipt,
10647
+ result: normalizeQueryResult(execution.result),
10543
10648
  }));
10544
10649
  }
10545
10650
  catch (error) {
10546
- const runtimeCode = semanticRuntimeErrorCode(error);
10547
- const status = runtimeCode ? 400 : 500;
10651
+ const runtimeCode = semanticExecutionFailureCode(error) ?? semanticRuntimeErrorCode(error);
10652
+ const status = runtimeCode === 'EXECUTION_TARGET_MISMATCH' ? 409 : runtimeCode ? 400 : 500;
10548
10653
  res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
10549
10654
  res.end(serializeJSON({
10550
10655
  error: error instanceof Error ? error.message : String(error),
10551
10656
  code: runtimeCode,
10552
- details: semanticRuntimeErrorDetails(error),
10657
+ details: semanticExecutionFailureDetails(error) ?? semanticRuntimeErrorDetails(error),
10553
10658
  hint: runtimeCode === 'SEMANTIC_RUNTIME_REQUIRED'
10554
10659
  ? 'Configure dbt Cloud Semantic Layer in Project & dbt settings, or install a compatible local MetricFlow runtime.'
10555
10660
  : undefined,
@@ -11108,7 +11213,24 @@ export async function startLocalServer(opts) {
11108
11213
  const prepared = prepareLocalExecution(executableSql, cellConnection, projectRoot, projectConfig);
11109
11214
  const app = loadRuntimeApp(projectRoot, typeof body.appId === 'string' ? body.appId : activePersonaAppId());
11110
11215
  assertAppAccess({ app, domain: resolved.domain ?? app?.domain, level: 'execute' });
11111
- const rawResult = await executor.executeQuery(prepared.sql, plan?.sqlParams ?? [], runtimeVariables({ ...(plan?.variables ?? {}), ...(invocation?.values ?? {}) }), prepared.connection);
11216
+ const semanticExecution = semanticCompose?.compiled
11217
+ ? await executeTargetBoundSemanticQuery({
11218
+ executor,
11219
+ connection: cellConnection,
11220
+ projectRoot,
11221
+ plannedAdapter: semanticCompose.compiled.engine,
11222
+ metricFlow: semanticCompose.compiled.engine === 'metricflow-cli'
11223
+ ? resolveMetricFlowTargetMetadata(projectRoot, projectConfig)
11224
+ : undefined,
11225
+ // Compilation already produced the exact pinned adapter result
11226
+ // used by the plan. The gateway validates target identity before
11227
+ // accepting that result for preflight and execution.
11228
+ compile: async () => semanticCompose.compiled,
11229
+ prepareSql: () => ({ sql: prepared.sql, connection: prepared.connection }),
11230
+ rowBound: semanticCompose.compiled.effectiveRequest.limit,
11231
+ })
11232
+ : null;
11233
+ const rawResult = semanticExecution?.result ?? await executor.executeQuery(prepared.sql, plan?.sqlParams ?? [], runtimeVariables({ ...(plan?.variables ?? {}), ...(invocation?.values ?? {}) }), prepared.connection);
11112
11234
  const normalized = normalizeQueryResult(rawResult);
11113
11235
  // Enforce the block's declared invariants against the result set. This
11114
11236
  // is additive: blocks without invariants produce `null` and the
@@ -11146,6 +11268,12 @@ export async function startLocalServer(opts) {
11146
11268
  chartConfig: plan?.chartConfig,
11147
11269
  tests: plan?.tests,
11148
11270
  result: normalized,
11271
+ ...(semanticExecution ? {
11272
+ semanticTrace: semanticExecution.semanticTrace,
11273
+ targetBinding: semanticExecution.targetBinding,
11274
+ executionReceipt: semanticExecution.executionReceipt,
11275
+ executedSql: semanticExecution.preparedSql,
11276
+ } : {}),
11149
11277
  ...(invocation ? {
11150
11278
  invocation: {
11151
11279
  resolvedParameters: invocation.resolvedParameters,
@@ -11178,8 +11306,13 @@ export async function startLocalServer(opts) {
11178
11306
  error: error instanceof Error ? error.message : String(error),
11179
11307
  });
11180
11308
  }
11181
- res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
11182
- res.end(serializeJSON({ error: error instanceof Error ? error.message : String(error) }));
11309
+ const code = semanticExecutionFailureCode(error) ?? semanticRuntimeErrorCode(error);
11310
+ res.writeHead(code === 'EXECUTION_TARGET_MISMATCH' ? 409 : 500, { 'Content-Type': 'application/json; charset=utf-8' });
11311
+ res.end(serializeJSON({
11312
+ error: error instanceof Error ? error.message : String(error),
11313
+ code,
11314
+ details: semanticExecutionFailureDetails(error) ?? semanticRuntimeErrorDetails(error),
11315
+ }));
11183
11316
  }
11184
11317
  return;
11185
11318
  }
@@ -14431,15 +14564,25 @@ function readUserPrefs(userPrefsPath) {
14431
14564
  function writeUserPrefs(userPrefsPath, prefs) {
14432
14565
  writeFileSync(userPrefsPath, JSON.stringify(prefs, null, 2) + '\n', 'utf-8');
14433
14566
  }
14434
- async function introspectSchema(executor, connection) {
14567
+ async function introspectSchema(executor, connection, options = {}) {
14435
14568
  let tables = [];
14436
- let columnsByPath = new Map();
14437
- // Tier 1: information_schema (PG, MySQL, Snowflake, MSSQL, DuckDB, Redshift, Fabric, Databricks)
14569
+ const columnsByPath = new Map();
14570
+ const limit = Math.min(1_000, Math.max(1, Math.floor(options.limit ?? 200)));
14571
+ const offset = Math.max(0, Math.floor(options.offset ?? 0));
14572
+ const escapedSearch = options.search?.trim().replace(/'/g, "''");
14573
+ const searchPredicate = escapedSearch
14574
+ ? ` AND (LOWER(table_schema) LIKE LOWER('%${escapedSearch}%') OR LOWER(table_name) LIKE LOWER('%${escapedSearch}%'))`
14575
+ : '';
14576
+ // Runtime inventory is table-first and bounded. Columns are loaded only for
14577
+ // a selected table through /api/describe-table; constructing one account-wide
14578
+ // table+column tree is an enterprise-scale OOM risk (PERF-001 / CTX-005).
14438
14579
  try {
14439
14580
  const catalogRows = await executor.executeQuery(`SELECT table_schema, table_name, table_type
14440
14581
  FROM information_schema.tables
14441
14582
  WHERE UPPER(table_schema) NOT IN ('INFORMATION_SCHEMA', 'PG_CATALOG')
14442
- ORDER BY table_schema, table_name`, [], {}, connection);
14583
+ ${searchPredicate}
14584
+ ORDER BY table_schema, table_name
14585
+ LIMIT ${limit} OFFSET ${offset}`, [], {}, connection);
14443
14586
  tables = catalogRows.rows.map((row) => {
14444
14587
  const schema = String(row['table_schema'] ?? row['TABLE_SCHEMA'] ?? 'default');
14445
14588
  const name = String(row['table_name'] ?? row['TABLE_NAME'] ?? '');
@@ -14447,22 +14590,6 @@ async function introspectSchema(executor, connection) {
14447
14590
  const path = schema ? `${schema}.${name}` : name;
14448
14591
  return { schema, name, path, type };
14449
14592
  });
14450
- const columnRows = await executor.executeQuery(`SELECT table_schema, table_name, column_name, data_type
14451
- FROM information_schema.columns
14452
- WHERE UPPER(table_schema) NOT IN ('INFORMATION_SCHEMA', 'PG_CATALOG')
14453
- ORDER BY table_schema, table_name, ordinal_position`, [], {}, connection);
14454
- columnsByPath = columnRows.rows.reduce((map, row) => {
14455
- const schema = String(row['table_schema'] ?? row['TABLE_SCHEMA'] ?? 'default');
14456
- const tableName = String(row['table_name'] ?? row['TABLE_NAME'] ?? '');
14457
- const path = schema ? `${schema}.${tableName}` : tableName;
14458
- const next = map.get(path) ?? [];
14459
- next.push({
14460
- name: String(row['column_name'] ?? row['COLUMN_NAME'] ?? ''),
14461
- type: String(row['data_type'] ?? row['DATA_TYPE'] ?? ''),
14462
- });
14463
- map.set(path, next);
14464
- return map;
14465
- }, new Map());
14466
14593
  return { tables, columnsByPath };
14467
14594
  }
14468
14595
  catch {
@@ -14473,23 +14600,18 @@ async function introspectSchema(executor, connection) {
14473
14600
  const connector = await executor.getConnector(connection);
14474
14601
  if (typeof connector.listTables === 'function') {
14475
14602
  const rawTables = await connector.listTables();
14476
- tables = rawTables.map((t) => {
14603
+ const normalizedSearch = options.search?.trim().toLowerCase();
14604
+ tables = rawTables
14605
+ .filter((table) => !normalizedSearch
14606
+ || table.schema.toLowerCase().includes(normalizedSearch)
14607
+ || table.name.toLowerCase().includes(normalizedSearch))
14608
+ .slice(offset, offset + limit)
14609
+ .map((t) => {
14477
14610
  const schema = t.schema || 'default';
14478
14611
  const path = t.schema ? `${t.schema}.${t.name}` : t.name;
14479
14612
  return { schema, name: t.name, path, type: t.type };
14480
14613
  });
14481
14614
  }
14482
- if (typeof connector.listColumns === 'function') {
14483
- const rawColumns = await connector.listColumns();
14484
- columnsByPath = rawColumns.reduce((map, col) => {
14485
- const schema = col.schema || 'default';
14486
- const path = schema ? `${schema}.${col.table}` : col.table;
14487
- const next = map.get(path) ?? [];
14488
- next.push({ name: col.name, type: col.dataType });
14489
- map.set(path, next);
14490
- return map;
14491
- }, new Map());
14492
- }
14493
14615
  }
14494
14616
  catch {
14495
14617
  // Tier 3: tables only, no columns — already have what we have
@@ -14499,9 +14621,9 @@ async function introspectSchema(executor, connection) {
14499
14621
  function buildDatabaseSchemaTree(projectRoot, executor, connection) {
14500
14622
  return (async () => {
14501
14623
  const dataFiles = scanDataFiles(projectRoot);
14502
- const { tables: dbTables, columnsByPath: dbColumnsByPath } = connection
14503
- ? await introspectSchema(executor, connection)
14504
- : { tables: [], columnsByPath: new Map() };
14624
+ const dbTables = connection
14625
+ ? (await introspectSchema(executor, connection, { limit: 1_000 })).tables
14626
+ : [];
14505
14627
  const schemaMap = new Map();
14506
14628
  for (const table of dbTables) {
14507
14629
  const schemaName = table.schema || 'default';
@@ -14523,13 +14645,8 @@ function buildDatabaseSchemaTree(projectRoot, executor, connection) {
14523
14645
  kind: 'table',
14524
14646
  path: table.path,
14525
14647
  type: table.type,
14526
- children: (dbColumnsByPath.get(table.path) ?? []).map((column) => ({
14527
- id: `db-column:${table.path}:${column.name}`,
14528
- label: column.name,
14529
- kind: 'column',
14530
- path: table.path,
14531
- type: column.type,
14532
- })),
14648
+ // Warehouse columns are loaded lazily for the selected table.
14649
+ children: [],
14533
14650
  })),
14534
14651
  }));
14535
14652
  // Eagerly resolve file columns via DuckDB DESCRIBE
@@ -14698,6 +14815,28 @@ function isDbtSemanticRuntime(projectConfig, detectedProvider, semanticLayer) {
14698
14815
  return true;
14699
14816
  return Boolean(semanticLayer?.listMetrics().some((metric) => metric.source?.provider === 'dbt'));
14700
14817
  }
14818
+ async function resolvePlannedSemanticAdapter(projectRoot, requested) {
14819
+ if (requested === 'dbt-cloud')
14820
+ return 'dbt-cloud';
14821
+ if (requested === 'metricflow' || requested === 'metricflow-cli')
14822
+ return 'metricflow-cli';
14823
+ if (requested === 'native')
14824
+ return 'native';
14825
+ return (await getSemanticRuntimeStatus(projectRoot, { probeConfiguredCloud: true })).active;
14826
+ }
14827
+ function resolveMetricFlowTargetMetadata(projectRoot, projectConfig) {
14828
+ const candidates = discoverDbtProfileConnections(projectRoot, projectConfig)
14829
+ .filter((candidate) => candidate.missingFields.length === 0);
14830
+ const candidate = candidates.find((item) => !item.warnings.some((warning) => warning.startsWith('Not the default dbt target'))) ?? (candidates.length === 1 ? candidates[0] : undefined);
14831
+ if (!candidate)
14832
+ return undefined;
14833
+ return {
14834
+ expectedTarget: configuredWarehouseTargetIdentity(candidate.connection),
14835
+ profileName: candidate.profileName,
14836
+ targetName: candidate.targetName,
14837
+ profilesPath: candidate.path,
14838
+ };
14839
+ }
14701
14840
  async function composeRuntimeSemanticQuery(request, semanticLayer, context) {
14702
14841
  return compileSemanticRuntimeQuery(request, {
14703
14842
  projectRoot: context.projectRoot,
@@ -14898,6 +15037,7 @@ async function composeSemanticBlockSqlForRuntime(source, semanticLayer, options)
14898
15037
  // no longer true for this run.
14899
15038
  diagnostics: native.diagnostics.filter((diagnostic) => diagnostic.code !== 'semantic_compose_failed'),
14900
15039
  semanticRefs: native.semanticRefs,
15040
+ compiled,
14901
15041
  };
14902
15042
  }
14903
15043
  catch (error) {
@@ -20573,6 +20713,33 @@ export function buildAgentSchemaContext(question, rows, options = {}) {
20573
20713
  .slice(0, limit)
20574
20714
  .map((entry) => entry.table);
20575
20715
  }
20716
+ const RUNTIME_SCHEMA_SEARCH_STOPWORDS = new Set([
20717
+ 'what', 'which', 'where', 'when', 'who', 'why', 'how', 'show', 'give', 'find',
20718
+ 'from', 'with', 'without', 'this', 'that', 'these', 'those', 'current', 'today',
20719
+ 'yesterday', 'last', 'year', 'month', 'week', 'day', 'top', 'bottom', 'their',
20720
+ 'have', 'does', 'into', 'than', 'then', 'also', 'all', 'the', 'and', 'for',
20721
+ ]);
20722
+ /**
20723
+ * Physical catalog evidence fallback. This is deliberately question-scoped and
20724
+ * result-bounded: dbt/DQL metadata remains the planning authority, while
20725
+ * information_schema confirms discoverable relations/columns in the selected
20726
+ * execution target. Tokens are reduced to safe identifier characters before
20727
+ * interpolation, so user text cannot become SQL syntax.
20728
+ */
20729
+ export function buildRuntimeSchemaSearchSql(question) {
20730
+ const terms = Array.from(agentSchemaTokens(question))
20731
+ .map((term) => term.toLowerCase().replace(/[^a-z0-9_]/g, ''))
20732
+ .filter((term) => term.length >= 3 && !RUNTIME_SCHEMA_SEARCH_STOPWORDS.has(term))
20733
+ .slice(0, 8);
20734
+ const predicate = terms.length > 0
20735
+ ? `\n AND (\n${terms.map((term) => ` LOWER(table_name) LIKE '%${term}%' OR LOWER(column_name) LIKE '%${term}%'`).join(' OR\n')}\n )`
20736
+ : '';
20737
+ return `SELECT table_schema, table_name, column_name, data_type
20738
+ FROM information_schema.columns
20739
+ WHERE UPPER(table_schema) NOT IN ('INFORMATION_SCHEMA', 'PG_CATALOG')${predicate}
20740
+ ORDER BY table_schema, table_name, ordinal_position
20741
+ LIMIT 600`;
20742
+ }
20576
20743
  function mergeAgentSchemaContexts(...contexts) {
20577
20744
  const byRelation = new Map();
20578
20745
  for (const table of contexts.flat()) {