@duckcodeailabs/dql-cli 1.10.2 → 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.
- package/dist/assets/dql-notebook/assets/{index-DVimJaeG.js → index-m7ziVzoC.js} +213 -210
- package/dist/assets/dql-notebook/index.html +1 -1
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +15 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/local-runtime.d.ts +8 -0
- package/dist/local-runtime.d.ts.map +1 -1
- package/dist/local-runtime.js +333 -140
- package/dist/local-runtime.js.map +1 -1
- package/dist/package.json +10 -10
- package/dist/semantic-execution/connection-identity.d.ts +11 -0
- package/dist/semantic-execution/connection-identity.d.ts.map +1 -0
- package/dist/semantic-execution/connection-identity.js +81 -0
- package/dist/semantic-execution/connection-identity.js.map +1 -0
- package/dist/semantic-execution/execution-gateway.d.ts +42 -0
- package/dist/semantic-execution/execution-gateway.d.ts.map +1 -0
- package/dist/semantic-execution/execution-gateway.js +203 -0
- package/dist/semantic-execution/execution-gateway.js.map +1 -0
- package/dist/semantic-execution/target-binding.d.ts +44 -0
- package/dist/semantic-execution/target-binding.d.ts.map +1 -0
- package/dist/semantic-execution/target-binding.js +185 -0
- package/dist/semantic-execution/target-binding.js.map +1 -0
- package/dist/semantic-runtime-settings.d.ts +7 -1
- package/dist/semantic-runtime-settings.d.ts.map +1 -1
- package/dist/semantic-runtime-settings.js +8 -1
- package/dist/semantic-runtime-settings.js.map +1 -1
- package/dist/semantic-runtime.d.ts +93 -2
- package/dist/semantic-runtime.d.ts.map +1 -1
- package/dist/semantic-runtime.js +335 -30
- package/dist/semantic-runtime.js.map +1 -1
- package/package.json +10 -10
package/dist/local-runtime.js
CHANGED
|
@@ -33,8 +33,12 @@ import { buildSemanticObjectDetail, buildSemanticTree, computeSyncDiff, loadSema
|
|
|
33
33
|
import { clearBlockStudioImportSessions, candidateToDqlSource, createBlockStudioImportSession, deleteBlockStudioImportSession, listBlockStudioImportSessions, loadBlockStudioImportSession, readBlockStudioImportCandidate, parameterizeSqlForDqlImport, updateBlockStudioImportCandidate, writeBlockStudioImportSession, writeBlockStudioImportCandidate, } from './block-studio-import.js';
|
|
34
34
|
import { MetricFlowUnavailableError, hasDbtSemanticManifest, } from "./metricflow.js";
|
|
35
35
|
import { ManagedMetricFlowInstaller, isMetricFlowWarehouseAdapter, metricFlowAdapterForDriver, } from './metricflow-installer.js';
|
|
36
|
-
import { compileSemanticRuntimeQuery, describeRuntimeCompatibility, explainMissingSemanticRuntime, getSemanticRuntimeStatus, semanticRuntimeErrorCode, semanticMetricExecutionCapability as runtimeMetricExecutionCapability, SemanticRuntimeRequiredError, testSemanticRuntimeDraft, } from './semantic-runtime.js';
|
|
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
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
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
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
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 (!
|
|
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
|
|
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 {
|
|
@@ -1320,9 +1340,31 @@ export async function startLocalServer(opts) {
|
|
|
1320
1340
|
} : {}),
|
|
1321
1341
|
...(semanticLayer ? {
|
|
1322
1342
|
semanticQueryCompiler: async (selection) => {
|
|
1323
|
-
const
|
|
1343
|
+
const requestedPath = decodeSemanticPathEvidenceId(request.selectedEvidenceId);
|
|
1344
|
+
const semanticRequest = {
|
|
1324
1345
|
...selection,
|
|
1325
1346
|
dimensions: selection.dimensions ?? [],
|
|
1347
|
+
};
|
|
1348
|
+
const requestedSemanticQuery = requestedPath
|
|
1349
|
+
? applySemanticPathSelection(semanticRequest, requestedPath.authoringReference, requestedPath.entityPath)
|
|
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,
|
|
1326
1368
|
}, {
|
|
1327
1369
|
projectRoot,
|
|
1328
1370
|
projectConfig,
|
|
@@ -1337,6 +1379,7 @@ export async function startLocalServer(opts) {
|
|
|
1337
1379
|
: projectConfig.dbt?.projectDir;
|
|
1338
1380
|
throw new SemanticRuntimeRequiredError(await explainMissingSemanticRuntime(projectRoot, dbtProjectPath));
|
|
1339
1381
|
}
|
|
1382
|
+
compiledSemanticQueries.set(executionFingerprint(compiled.sql), compiled);
|
|
1340
1383
|
return {
|
|
1341
1384
|
sql: compiled.sql,
|
|
1342
1385
|
engine: compiled.engine,
|
|
@@ -1348,6 +1391,10 @@ export async function startLocalServer(opts) {
|
|
|
1348
1391
|
? { timeDimension: compiled.effectiveRequest.timeDimension }
|
|
1349
1392
|
: {}),
|
|
1350
1393
|
},
|
|
1394
|
+
trace: {
|
|
1395
|
+
...compiled.semanticTrace,
|
|
1396
|
+
...(targetBinding ? { targetBinding } : {}),
|
|
1397
|
+
},
|
|
1351
1398
|
};
|
|
1352
1399
|
},
|
|
1353
1400
|
} : {}),
|
|
@@ -1844,6 +1891,9 @@ export async function startLocalServer(opts) {
|
|
|
1844
1891
|
? 'The governed query could not be executed.'
|
|
1845
1892
|
: governedAnswer.route?.label ?? (isCertified ? 'Answered from certified DQL context.' : isExploratory ? 'Exploratory DBT-grounded analysis requires review.' : 'Answered with review-required generated analysis.'),
|
|
1846
1893
|
answer: synthesizedAnswer ?? governedAnswer.answer ?? governedAnswer.text,
|
|
1894
|
+
...(governedAnswer.clarificationOptions?.length
|
|
1895
|
+
? { clarificationOptions: governedAnswer.clarificationOptions }
|
|
1896
|
+
: {}),
|
|
1847
1897
|
status,
|
|
1848
1898
|
trustState,
|
|
1849
1899
|
stopReason,
|
|
@@ -1853,9 +1903,13 @@ export async function startLocalServer(opts) {
|
|
|
1853
1903
|
// A refusal still keeps the DQL draft the answer loop produced (when any),
|
|
1854
1904
|
// so the "Review DQL draft" next-action isn't a dead link and the user can
|
|
1855
1905
|
// see the SQL that was about to run. Provider outages carry no draft.
|
|
1856
|
-
? (governedAnswer.
|
|
1857
|
-
? [agentRunArtifact('
|
|
1858
|
-
|
|
1906
|
+
? (governedAnswer.semanticExecutionTrace
|
|
1907
|
+
? [agentRunArtifact('answer', governedAnswer.refusalCode === 'ambiguous'
|
|
1908
|
+
? 'Semantic path selection required'
|
|
1909
|
+
: 'Semantic compilation details', governedAnswer, undefined, needsClarification ? 'not_applicable' : 'review_required')]
|
|
1910
|
+
: governedAnswer.dqlArtifact && !isProviderError && !isGroundingGap && !isModelDeclined && !isPolicyBlocked
|
|
1911
|
+
? [agentRunArtifact('dql_block_draft', 'DQL draft (review required)', governedAnswer.dqlArtifact, undefined, 'review_required')]
|
|
1912
|
+
: [])
|
|
1859
1913
|
: [agentRunArtifact('answer', isCertified ? 'Certified answer' : isSemantic ? 'Governed semantic answer' : isExploratory ? 'Exploratory DBT-grounded answer' : 'Review-required answer', governedAnswer, governedAnswer.sourceCertifiedBlock ?? governedAnswer.block?.name, isCertified ? 'certified' : isSemantic ? 'governed' : 'review_required')],
|
|
1860
1914
|
evaluations: [
|
|
1861
1915
|
agentRunEvaluation('route-decision', 'Route decision', true, 'info', routeDecision?.reason ?? 'Routed request to governed answer.', {
|
|
@@ -2766,7 +2820,10 @@ export async function startLocalServer(opts) {
|
|
|
2766
2820
|
throw new Error(`I need values for: ${invocation.unresolvedParameters.join(', ')}.`);
|
|
2767
2821
|
}
|
|
2768
2822
|
const activeConnection = requireActiveConnection();
|
|
2769
|
-
const
|
|
2823
|
+
const precompiledSemanticQuery = Boolean(extractBlockStudioSql(source));
|
|
2824
|
+
const tableMapping = !precompiledSemanticQuery
|
|
2825
|
+
? await resolveSemanticTableMapping(executor, activeConnection, semanticLayer)
|
|
2826
|
+
: undefined;
|
|
2770
2827
|
const semanticCompose = semanticLayer
|
|
2771
2828
|
? await composeSemanticBlockSqlForRuntime(source, semanticLayer, {
|
|
2772
2829
|
driver: activeConnection.driver,
|
|
@@ -2795,7 +2852,24 @@ export async function startLocalServer(opts) {
|
|
|
2795
2852
|
const app = loadRuntimeApp(projectRoot, activePersonaAppId());
|
|
2796
2853
|
const sourceDomain = metadata.domain ?? source.match(/\bdomain\s*=\s*"([^"]+)"/i)?.[1];
|
|
2797
2854
|
assertAppAccess({ app, domain: sourceDomain ?? app?.domain, level: 'execute' });
|
|
2798
|
-
const
|
|
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);
|
|
2799
2873
|
const normalized = normalizeQueryResult(rawResult);
|
|
2800
2874
|
const executionReceipt = createDqlArtifactExecutionReceipt(source, executionSql, invocation.values, normalized);
|
|
2801
2875
|
return {
|
|
@@ -2810,6 +2884,11 @@ export async function startLocalServer(opts) {
|
|
|
2810
2884
|
parameters: invocation.resolvedParameters,
|
|
2811
2885
|
auditId: invocation.auditId,
|
|
2812
2886
|
executionReceipt,
|
|
2887
|
+
...(semanticExecution ? {
|
|
2888
|
+
semanticExecutionReceipt: semanticExecution.executionReceipt,
|
|
2889
|
+
semanticTargetBinding: semanticExecution.targetBinding,
|
|
2890
|
+
semanticTrace: semanticExecution.semanticTrace,
|
|
2891
|
+
} : {}),
|
|
2813
2892
|
};
|
|
2814
2893
|
};
|
|
2815
2894
|
const executeCertifiedBlockByNameForAgent = async (blockName, invocationInput, requireCertified = false) => {
|
|
@@ -2998,14 +3077,10 @@ export async function startLocalServer(opts) {
|
|
|
2998
3077
|
const scanRuntimeSchema = async () => {
|
|
2999
3078
|
if (!connection)
|
|
3000
3079
|
return { ranked: [], snapshot: [] };
|
|
3001
|
-
const result = await executor.
|
|
3002
|
-
FROM information_schema.columns
|
|
3003
|
-
WHERE UPPER(table_schema) NOT IN ('INFORMATION_SCHEMA', 'PG_CATALOG')
|
|
3004
|
-
ORDER BY table_schema, table_name, ordinal_position
|
|
3005
|
-
LIMIT 2000`, [], runtimeVariables({}), connection);
|
|
3080
|
+
const result = await executor.executePositional(buildRuntimeSchemaSearchSql(question), [], connection, { maxRows: 600, maxBytes: 2 * 1024 * 1024, batchSize: 200, deadlineMs: 30_000 });
|
|
3006
3081
|
return {
|
|
3007
3082
|
ranked: buildAgentSchemaContext(question, result.rows),
|
|
3008
|
-
snapshot: buildAgentSchemaContext(question, result.rows, { includeUnscored: true, limit:
|
|
3083
|
+
snapshot: buildAgentSchemaContext(question, result.rows, { includeUnscored: true, limit: 100 }),
|
|
3009
3084
|
};
|
|
3010
3085
|
};
|
|
3011
3086
|
const catalogContext = await buildAgentSchemaContextFromCatalog(projectRoot, question, preparedContextPack).catch(() => []);
|
|
@@ -3027,7 +3102,7 @@ export async function startLocalServer(opts) {
|
|
|
3027
3102
|
const enriched = valueGrounding.mode === 'safe_automatic'
|
|
3028
3103
|
? await enrichAgentSchemaContextWithValueMatches(question, schemaContext.ranked, executor, connection, valueGrounding.searchSafeColumns)
|
|
3029
3104
|
: schemaContext.ranked;
|
|
3030
|
-
recordAgentRuntimeSchemaSnapshot(projectRoot, schemaContext.snapshot, '
|
|
3105
|
+
recordAgentRuntimeSchemaSnapshot(projectRoot, schemaContext.snapshot, 'question-scoped information_schema search');
|
|
3031
3106
|
return enriched;
|
|
3032
3107
|
}
|
|
3033
3108
|
catch {
|
|
@@ -7431,8 +7506,15 @@ export async function startLocalServer(opts) {
|
|
|
7431
7506
|
refreshedAt: dataset.refreshedAt,
|
|
7432
7507
|
trustState: dataset.trustState,
|
|
7433
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();
|
|
7434
7512
|
const { tables, columnsByPath } = connection
|
|
7435
|
-
? await introspectSchema(executor, connection
|
|
7513
|
+
? await introspectSchema(executor, connection, {
|
|
7514
|
+
limit: schemaLimit,
|
|
7515
|
+
offset: schemaOffset,
|
|
7516
|
+
search: schemaSearch,
|
|
7517
|
+
})
|
|
7436
7518
|
: { tables: [], columnsByPath: new Map() };
|
|
7437
7519
|
const dbTables = tables.map((t) => ({
|
|
7438
7520
|
name: t.path,
|
|
@@ -9330,7 +9412,9 @@ export async function startLocalServer(opts) {
|
|
|
9330
9412
|
try {
|
|
9331
9413
|
const body = await readJSON(req);
|
|
9332
9414
|
const result = await testSemanticRuntimeDraft(projectRoot, body);
|
|
9333
|
-
const
|
|
9415
|
+
const targetConnection = requireActiveConnection(connection);
|
|
9416
|
+
const executionTarget = await observeWarehouseTargetIdentity(executor, targetConnection);
|
|
9417
|
+
const settings = saveTestedSemanticRuntimeSettings(projectRoot, body, result, executionTarget);
|
|
9334
9418
|
const runtime = await getSemanticRuntimeStatus(projectRoot);
|
|
9335
9419
|
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
9336
9420
|
res.end(serializeJSON({ ok: true, ...settings, runtime }));
|
|
@@ -10399,25 +10483,41 @@ export async function startLocalServer(opts) {
|
|
|
10399
10483
|
const { metrics = [], dimensions = [], filters = [], limit, timeDimension, orderBy, savedQuery, engine } = body;
|
|
10400
10484
|
// Resolve which connection to use — request can override default
|
|
10401
10485
|
const targetConnection = requireActiveConnection(isConnectionConfig(body.connection) ? body.connection : connection);
|
|
10402
|
-
const
|
|
10403
|
-
const
|
|
10404
|
-
|
|
10405
|
-
|
|
10406
|
-
|
|
10407
|
-
|
|
10408
|
-
|
|
10409
|
-
timeDimension,
|
|
10410
|
-
orderBy,
|
|
10411
|
-
savedQuery,
|
|
10412
|
-
engine,
|
|
10413
|
-
}, 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,
|
|
10414
10493
|
projectRoot,
|
|
10415
|
-
|
|
10416
|
-
|
|
10417
|
-
|
|
10418
|
-
|
|
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,
|
|
10419
10519
|
});
|
|
10420
|
-
if (!
|
|
10520
|
+
if (!execution) {
|
|
10421
10521
|
const provider = semanticConfig?.provider ?? semanticDetectedProvider ?? 'dql';
|
|
10422
10522
|
const semanticRuntime = await getSemanticRuntimeStatus(projectRoot, { probeConfiguredCloud: true });
|
|
10423
10523
|
const blocked = metrics.map((metricName) => ({
|
|
@@ -10434,17 +10534,20 @@ export async function startLocalServer(opts) {
|
|
|
10434
10534
|
}));
|
|
10435
10535
|
return;
|
|
10436
10536
|
}
|
|
10437
|
-
// Execute the composed SQL against the resolved connection
|
|
10438
|
-
const prepared = prepareLocalExecution(composed.sql, targetConnection, projectRoot, projectConfig);
|
|
10439
|
-
const result = await executor.executeQuery(prepared.sql, [], {}, prepared.connection);
|
|
10440
10537
|
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
10441
10538
|
res.end(serializeJSON({
|
|
10442
|
-
sql:
|
|
10443
|
-
|
|
10444
|
-
|
|
10445
|
-
|
|
10446
|
-
|
|
10447
|
-
|
|
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),
|
|
10448
10551
|
}));
|
|
10449
10552
|
}
|
|
10450
10553
|
catch (error) {
|
|
@@ -10453,12 +10556,13 @@ export async function startLocalServer(opts) {
|
|
|
10453
10556
|
res.end(serializeJSON({ error: error.message, code: 'unauthorized' }));
|
|
10454
10557
|
return;
|
|
10455
10558
|
}
|
|
10456
|
-
const runtimeCode = semanticRuntimeErrorCode(error);
|
|
10457
|
-
const status = runtimeCode ? 400 : 500;
|
|
10559
|
+
const runtimeCode = semanticExecutionFailureCode(error) ?? semanticRuntimeErrorCode(error);
|
|
10560
|
+
const status = runtimeCode === 'EXECUTION_TARGET_MISMATCH' ? 409 : runtimeCode ? 400 : 500;
|
|
10458
10561
|
res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
10459
10562
|
res.end(serializeJSON({
|
|
10460
10563
|
error: error instanceof Error ? error.message : String(error),
|
|
10461
10564
|
code: runtimeCode,
|
|
10565
|
+
details: semanticExecutionFailureDetails(error) ?? semanticRuntimeErrorDetails(error),
|
|
10462
10566
|
hint: runtimeCode === 'SEMANTIC_RUNTIME_REQUIRED'
|
|
10463
10567
|
? 'Configure dbt Cloud Semantic Layer in Project & dbt settings, or install a compatible local MetricFlow runtime.'
|
|
10464
10568
|
: undefined,
|
|
@@ -10476,25 +10580,41 @@ export async function startLocalServer(opts) {
|
|
|
10476
10580
|
const body = await readJSON(req);
|
|
10477
10581
|
const { metrics = [], dimensions = [], filters = [], limit, timeDimension, orderBy, savedQuery, engine } = body;
|
|
10478
10582
|
const targetConnection = requireActiveConnection(isConnectionConfig(body.connection) ? body.connection : connection);
|
|
10479
|
-
const
|
|
10480
|
-
const
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
|
|
10484
|
-
|
|
10485
|
-
|
|
10486
|
-
timeDimension,
|
|
10487
|
-
orderBy,
|
|
10488
|
-
savedQuery,
|
|
10489
|
-
engine,
|
|
10490
|
-
}, 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,
|
|
10491
10590
|
projectRoot,
|
|
10492
|
-
|
|
10493
|
-
|
|
10494
|
-
|
|
10495
|
-
|
|
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,
|
|
10496
10616
|
});
|
|
10497
|
-
if (!
|
|
10617
|
+
if (!execution) {
|
|
10498
10618
|
const provider = semanticConfig?.provider ?? semanticDetectedProvider ?? 'dql';
|
|
10499
10619
|
const semanticRuntime = await getSemanticRuntimeStatus(projectRoot, { probeConfiguredCloud: true });
|
|
10500
10620
|
const blocked = metrics.map((metricName) => ({
|
|
@@ -10511,25 +10631,30 @@ export async function startLocalServer(opts) {
|
|
|
10511
10631
|
}));
|
|
10512
10632
|
return;
|
|
10513
10633
|
}
|
|
10514
|
-
const prepared = prepareLocalExecution(composed.sql, targetConnection, projectRoot, projectConfig);
|
|
10515
|
-
const result = await executor.executeQuery(prepared.sql, [], {}, prepared.connection);
|
|
10516
10634
|
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
10517
10635
|
res.end(serializeJSON({
|
|
10518
|
-
sql:
|
|
10519
|
-
|
|
10520
|
-
|
|
10521
|
-
|
|
10522
|
-
|
|
10523
|
-
|
|
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),
|
|
10524
10648
|
}));
|
|
10525
10649
|
}
|
|
10526
10650
|
catch (error) {
|
|
10527
|
-
const runtimeCode = semanticRuntimeErrorCode(error);
|
|
10528
|
-
const status = runtimeCode ? 400 : 500;
|
|
10651
|
+
const runtimeCode = semanticExecutionFailureCode(error) ?? semanticRuntimeErrorCode(error);
|
|
10652
|
+
const status = runtimeCode === 'EXECUTION_TARGET_MISMATCH' ? 409 : runtimeCode ? 400 : 500;
|
|
10529
10653
|
res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
10530
10654
|
res.end(serializeJSON({
|
|
10531
10655
|
error: error instanceof Error ? error.message : String(error),
|
|
10532
10656
|
code: runtimeCode,
|
|
10657
|
+
details: semanticExecutionFailureDetails(error) ?? semanticRuntimeErrorDetails(error),
|
|
10533
10658
|
hint: runtimeCode === 'SEMANTIC_RUNTIME_REQUIRED'
|
|
10534
10659
|
? 'Configure dbt Cloud Semantic Layer in Project & dbt settings, or install a compatible local MetricFlow runtime.'
|
|
10535
10660
|
: undefined,
|
|
@@ -10605,6 +10730,7 @@ export async function startLocalServer(opts) {
|
|
|
10605
10730
|
res.end(serializeJSON({
|
|
10606
10731
|
error: error instanceof Error ? error.message : String(error),
|
|
10607
10732
|
code: runtimeCode,
|
|
10733
|
+
details: semanticRuntimeErrorDetails(error),
|
|
10608
10734
|
hint: runtimeCode === 'SEMANTIC_RUNTIME_REQUIRED'
|
|
10609
10735
|
? 'Configure dbt Cloud Semantic Layer in Project & dbt settings, or install a compatible local MetricFlow runtime.'
|
|
10610
10736
|
: undefined,
|
|
@@ -11087,7 +11213,24 @@ export async function startLocalServer(opts) {
|
|
|
11087
11213
|
const prepared = prepareLocalExecution(executableSql, cellConnection, projectRoot, projectConfig);
|
|
11088
11214
|
const app = loadRuntimeApp(projectRoot, typeof body.appId === 'string' ? body.appId : activePersonaAppId());
|
|
11089
11215
|
assertAppAccess({ app, domain: resolved.domain ?? app?.domain, level: 'execute' });
|
|
11090
|
-
const
|
|
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);
|
|
11091
11234
|
const normalized = normalizeQueryResult(rawResult);
|
|
11092
11235
|
// Enforce the block's declared invariants against the result set. This
|
|
11093
11236
|
// is additive: blocks without invariants produce `null` and the
|
|
@@ -11125,6 +11268,12 @@ export async function startLocalServer(opts) {
|
|
|
11125
11268
|
chartConfig: plan?.chartConfig,
|
|
11126
11269
|
tests: plan?.tests,
|
|
11127
11270
|
result: normalized,
|
|
11271
|
+
...(semanticExecution ? {
|
|
11272
|
+
semanticTrace: semanticExecution.semanticTrace,
|
|
11273
|
+
targetBinding: semanticExecution.targetBinding,
|
|
11274
|
+
executionReceipt: semanticExecution.executionReceipt,
|
|
11275
|
+
executedSql: semanticExecution.preparedSql,
|
|
11276
|
+
} : {}),
|
|
11128
11277
|
...(invocation ? {
|
|
11129
11278
|
invocation: {
|
|
11130
11279
|
resolvedParameters: invocation.resolvedParameters,
|
|
@@ -11157,8 +11306,13 @@ export async function startLocalServer(opts) {
|
|
|
11157
11306
|
error: error instanceof Error ? error.message : String(error),
|
|
11158
11307
|
});
|
|
11159
11308
|
}
|
|
11160
|
-
|
|
11161
|
-
res.
|
|
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
|
+
}));
|
|
11162
11316
|
}
|
|
11163
11317
|
return;
|
|
11164
11318
|
}
|
|
@@ -14410,15 +14564,25 @@ function readUserPrefs(userPrefsPath) {
|
|
|
14410
14564
|
function writeUserPrefs(userPrefsPath, prefs) {
|
|
14411
14565
|
writeFileSync(userPrefsPath, JSON.stringify(prefs, null, 2) + '\n', 'utf-8');
|
|
14412
14566
|
}
|
|
14413
|
-
async function introspectSchema(executor, connection) {
|
|
14567
|
+
async function introspectSchema(executor, connection, options = {}) {
|
|
14414
14568
|
let tables = [];
|
|
14415
|
-
|
|
14416
|
-
|
|
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).
|
|
14417
14579
|
try {
|
|
14418
14580
|
const catalogRows = await executor.executeQuery(`SELECT table_schema, table_name, table_type
|
|
14419
14581
|
FROM information_schema.tables
|
|
14420
14582
|
WHERE UPPER(table_schema) NOT IN ('INFORMATION_SCHEMA', 'PG_CATALOG')
|
|
14421
|
-
|
|
14583
|
+
${searchPredicate}
|
|
14584
|
+
ORDER BY table_schema, table_name
|
|
14585
|
+
LIMIT ${limit} OFFSET ${offset}`, [], {}, connection);
|
|
14422
14586
|
tables = catalogRows.rows.map((row) => {
|
|
14423
14587
|
const schema = String(row['table_schema'] ?? row['TABLE_SCHEMA'] ?? 'default');
|
|
14424
14588
|
const name = String(row['table_name'] ?? row['TABLE_NAME'] ?? '');
|
|
@@ -14426,22 +14590,6 @@ async function introspectSchema(executor, connection) {
|
|
|
14426
14590
|
const path = schema ? `${schema}.${name}` : name;
|
|
14427
14591
|
return { schema, name, path, type };
|
|
14428
14592
|
});
|
|
14429
|
-
const columnRows = await executor.executeQuery(`SELECT table_schema, table_name, column_name, data_type
|
|
14430
|
-
FROM information_schema.columns
|
|
14431
|
-
WHERE UPPER(table_schema) NOT IN ('INFORMATION_SCHEMA', 'PG_CATALOG')
|
|
14432
|
-
ORDER BY table_schema, table_name, ordinal_position`, [], {}, connection);
|
|
14433
|
-
columnsByPath = columnRows.rows.reduce((map, row) => {
|
|
14434
|
-
const schema = String(row['table_schema'] ?? row['TABLE_SCHEMA'] ?? 'default');
|
|
14435
|
-
const tableName = String(row['table_name'] ?? row['TABLE_NAME'] ?? '');
|
|
14436
|
-
const path = schema ? `${schema}.${tableName}` : tableName;
|
|
14437
|
-
const next = map.get(path) ?? [];
|
|
14438
|
-
next.push({
|
|
14439
|
-
name: String(row['column_name'] ?? row['COLUMN_NAME'] ?? ''),
|
|
14440
|
-
type: String(row['data_type'] ?? row['DATA_TYPE'] ?? ''),
|
|
14441
|
-
});
|
|
14442
|
-
map.set(path, next);
|
|
14443
|
-
return map;
|
|
14444
|
-
}, new Map());
|
|
14445
14593
|
return { tables, columnsByPath };
|
|
14446
14594
|
}
|
|
14447
14595
|
catch {
|
|
@@ -14452,23 +14600,18 @@ async function introspectSchema(executor, connection) {
|
|
|
14452
14600
|
const connector = await executor.getConnector(connection);
|
|
14453
14601
|
if (typeof connector.listTables === 'function') {
|
|
14454
14602
|
const rawTables = await connector.listTables();
|
|
14455
|
-
|
|
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) => {
|
|
14456
14610
|
const schema = t.schema || 'default';
|
|
14457
14611
|
const path = t.schema ? `${t.schema}.${t.name}` : t.name;
|
|
14458
14612
|
return { schema, name: t.name, path, type: t.type };
|
|
14459
14613
|
});
|
|
14460
14614
|
}
|
|
14461
|
-
if (typeof connector.listColumns === 'function') {
|
|
14462
|
-
const rawColumns = await connector.listColumns();
|
|
14463
|
-
columnsByPath = rawColumns.reduce((map, col) => {
|
|
14464
|
-
const schema = col.schema || 'default';
|
|
14465
|
-
const path = schema ? `${schema}.${col.table}` : col.table;
|
|
14466
|
-
const next = map.get(path) ?? [];
|
|
14467
|
-
next.push({ name: col.name, type: col.dataType });
|
|
14468
|
-
map.set(path, next);
|
|
14469
|
-
return map;
|
|
14470
|
-
}, new Map());
|
|
14471
|
-
}
|
|
14472
14615
|
}
|
|
14473
14616
|
catch {
|
|
14474
14617
|
// Tier 3: tables only, no columns — already have what we have
|
|
@@ -14478,9 +14621,9 @@ async function introspectSchema(executor, connection) {
|
|
|
14478
14621
|
function buildDatabaseSchemaTree(projectRoot, executor, connection) {
|
|
14479
14622
|
return (async () => {
|
|
14480
14623
|
const dataFiles = scanDataFiles(projectRoot);
|
|
14481
|
-
const
|
|
14482
|
-
? await introspectSchema(executor, connection)
|
|
14483
|
-
:
|
|
14624
|
+
const dbTables = connection
|
|
14625
|
+
? (await introspectSchema(executor, connection, { limit: 1_000 })).tables
|
|
14626
|
+
: [];
|
|
14484
14627
|
const schemaMap = new Map();
|
|
14485
14628
|
for (const table of dbTables) {
|
|
14486
14629
|
const schemaName = table.schema || 'default';
|
|
@@ -14502,13 +14645,8 @@ function buildDatabaseSchemaTree(projectRoot, executor, connection) {
|
|
|
14502
14645
|
kind: 'table',
|
|
14503
14646
|
path: table.path,
|
|
14504
14647
|
type: table.type,
|
|
14505
|
-
|
|
14506
|
-
|
|
14507
|
-
label: column.name,
|
|
14508
|
-
kind: 'column',
|
|
14509
|
-
path: table.path,
|
|
14510
|
-
type: column.type,
|
|
14511
|
-
})),
|
|
14648
|
+
// Warehouse columns are loaded lazily for the selected table.
|
|
14649
|
+
children: [],
|
|
14512
14650
|
})),
|
|
14513
14651
|
}));
|
|
14514
14652
|
// Eagerly resolve file columns via DuckDB DESCRIBE
|
|
@@ -14677,6 +14815,28 @@ function isDbtSemanticRuntime(projectConfig, detectedProvider, semanticLayer) {
|
|
|
14677
14815
|
return true;
|
|
14678
14816
|
return Boolean(semanticLayer?.listMetrics().some((metric) => metric.source?.provider === 'dbt'));
|
|
14679
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
|
+
}
|
|
14680
14840
|
async function composeRuntimeSemanticQuery(request, semanticLayer, context) {
|
|
14681
14841
|
return compileSemanticRuntimeQuery(request, {
|
|
14682
14842
|
projectRoot: context.projectRoot,
|
|
@@ -14688,13 +14848,18 @@ async function composeRuntimeSemanticQuery(request, semanticLayer, context) {
|
|
|
14688
14848
|
});
|
|
14689
14849
|
}
|
|
14690
14850
|
function composeRuntimeSemanticQueryNative(request, semanticLayer, context) {
|
|
14851
|
+
const stripPath = (value) => parseSemanticDimensionSelection(value).reference;
|
|
14691
14852
|
const composed = semanticLayer.composeQuery({
|
|
14692
14853
|
metrics: request.metrics,
|
|
14693
|
-
dimensions: request.dimensions,
|
|
14694
|
-
filters: request.filters
|
|
14854
|
+
dimensions: request.dimensions.map(stripPath),
|
|
14855
|
+
filters: request.filters?.map((filter) => filter.dimension
|
|
14856
|
+
? { ...filter, dimension: stripPath(filter.dimension) }
|
|
14857
|
+
: filter),
|
|
14695
14858
|
limit: request.limit,
|
|
14696
|
-
timeDimension: request.timeDimension
|
|
14697
|
-
|
|
14859
|
+
timeDimension: request.timeDimension
|
|
14860
|
+
? { ...request.timeDimension, name: stripPath(request.timeDimension.name) }
|
|
14861
|
+
: undefined,
|
|
14862
|
+
orderBy: request.orderBy?.map((order) => ({ ...order, name: stripPath(order.name) })),
|
|
14698
14863
|
driver: context.driver,
|
|
14699
14864
|
tableMapping: context.tableMapping,
|
|
14700
14865
|
});
|
|
@@ -14737,8 +14902,8 @@ function composeSemanticBlockSql(source, semanticLayer, options) {
|
|
|
14737
14902
|
}
|
|
14738
14903
|
const refValidation = semanticLayer.validateReferences([
|
|
14739
14904
|
...metrics,
|
|
14740
|
-
...config.dimensions,
|
|
14741
|
-
...(config.timeDimension ? [config.timeDimension] : []),
|
|
14905
|
+
...config.dimensions.map((dimension) => parseSemanticDimensionSelection(dimension).reference),
|
|
14906
|
+
...(config.timeDimension ? [parseSemanticDimensionSelection(config.timeDimension).reference] : []),
|
|
14742
14907
|
]);
|
|
14743
14908
|
for (const unknown of refValidation.unknown) {
|
|
14744
14909
|
diagnostics.push({
|
|
@@ -14872,6 +15037,7 @@ async function composeSemanticBlockSqlForRuntime(source, semanticLayer, options)
|
|
|
14872
15037
|
// no longer true for this run.
|
|
14873
15038
|
diagnostics: native.diagnostics.filter((diagnostic) => diagnostic.code !== 'semantic_compose_failed'),
|
|
14874
15039
|
semanticRefs: native.semanticRefs,
|
|
15040
|
+
compiled,
|
|
14875
15041
|
};
|
|
14876
15042
|
}
|
|
14877
15043
|
catch (error) {
|
|
@@ -20547,6 +20713,33 @@ export function buildAgentSchemaContext(question, rows, options = {}) {
|
|
|
20547
20713
|
.slice(0, limit)
|
|
20548
20714
|
.map((entry) => entry.table);
|
|
20549
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
|
+
}
|
|
20550
20743
|
function mergeAgentSchemaContexts(...contexts) {
|
|
20551
20744
|
const byRelation = new Map();
|
|
20552
20745
|
for (const table of contexts.flat()) {
|