fix(scan): unblock relationship discovery on Snowflake

Two adjacent bugs prevented the scan's relationship pipeline from producing
any joins on a Snowflake warehouse:

- relationship-profiling.ts fell through to a default `GROUP_CONCAT` branch
  for unknown drivers. Snowflake has no GROUP_CONCAT, so every per-table
  profile query failed with "Unknown function GROUP_CONCAT". Add an explicit
  Snowflake branch that uses LISTAGG with a literal '\x1f' delimiter
  (Snowflake requires the delimiter to be a constant, so CHR(31) is rejected).
- description-generation.ts destructured `connector.sampleTable` and
  `connector.sampleColumn` into bare locals, losing the `this` binding when
  the class-method connectors (Snowflake, Postgres, MySQL) were invoked.
  Every sample call threw "Cannot read properties of undefined (reading
  'assertConnection')" and degraded LLM descriptions to metadata-only
  prompts. Call the methods through the connector instead.

Without these, even after the primary-key probe is allowed to fail softly,
the scan ends up with 0 validated relationships and an empty `joins:` block
in every shard YAML.
This commit is contained in:
Andrey Avtomonov 2026-05-22 15:01:14 +02:00
parent 1349af1702
commit b664d5c3d8
2 changed files with 8 additions and 5 deletions

View file

@ -463,11 +463,11 @@ export class KtxDescriptionGenerator {
}
}
const sampleTable = input.connector.sampleTable;
const connector = input.connector;
let sampleData: KtxTableSampleResult | null = null;
let fallbackReason: 'capability_missing' | 'sampling_failed' | 'empty_sample' | null = null;
if (!sampleTable) {
if (!connector.sampleTable) {
fallbackReason = 'capability_missing';
this.logger?.warn('KTX scan connector does not support table sampling; falling back to metadata-only prompt', {
connectorId: input.connector.id,
@ -484,7 +484,7 @@ export class KtxDescriptionGenerator {
try {
sampleData = await retryAsync(
() =>
sampleTable(
connector.sampleTable!(
{
connectionId: input.connectionId,
table: tableRef,
@ -684,11 +684,11 @@ export class KtxDescriptionGenerator {
});
columnValues = [];
} else {
const sampleColumn = input.connector.sampleColumn;
const connector = input.connector;
try {
const sample = await retryAsync(
() =>
sampleColumn(
connector.sampleColumn!(
{
connectionId: input.connectionId,
table: tableRef,

View file

@ -227,6 +227,9 @@ function sampleAggregateSql(driver: KtxConnectionDriver, innerSql: string): stri
if (driver === 'clickhouse') {
return `(SELECT arrayStringConcat(groupArray(toString(value)), '\\x1F') FROM (${innerSql}) AS relationship_profile_values)`;
}
if (driver === 'snowflake') {
return `(SELECT LISTAGG(CAST(value AS VARCHAR), '\\x1f') FROM (${innerSql}) AS relationship_profile_values)`;
}
return `(SELECT GROUP_CONCAT(CAST(value AS TEXT), char(31)) FROM (${innerSql}) AS relationship_profile_values)`;
}