From a9760f5341d7e05fca7258c3d4cf859f720a0a75 Mon Sep 17 00:00:00 2001 From: Vijay Yadav Date: Tue, 24 Mar 2026 00:12:36 -0400 Subject: [PATCH 1/3] fix: complete migration from string interpolation to parameterized query binds Replace all remaining .replace("{placeholder}", ...) patterns with parameterized ? placeholders and binds arrays across finops and schema modules. Also migrates warehouse-advisor.ts which was missed in #277. Updates tests to verify binds are passed correctly. Fixes #290 --- .../altimate/native/finops/credit-analyzer.ts | 7 +--- .../altimate/native/finops/query-history.ts | 13 ++----- .../src/altimate/native/finops/role-access.ts | 28 ++++---------- .../native/finops/warehouse-advisor.ts | 38 +++++++++---------- .../src/altimate/native/schema/tags.ts | 6 +-- .../test/altimate/schema-finops-dbt.test.ts | 18 +++++---- 6 files changed, 46 insertions(+), 64 deletions(-) diff --git a/packages/opencode/src/altimate/native/finops/credit-analyzer.ts b/packages/opencode/src/altimate/native/finops/credit-analyzer.ts index f8d22cc27b..f19b2bddbb 100644 --- a/packages/opencode/src/altimate/native/finops/credit-analyzer.ts +++ b/packages/opencode/src/altimate/native/finops/credit-analyzer.ts @@ -27,10 +27,6 @@ SELECT AVG(credits_used) as avg_credits_per_query FROM SNOWFLAKE.ACCOUNT_USAGE.WAREHOUSE_METERING_HISTORY WHERE start_time >= DATEADD('day', ?, CURRENT_TIMESTAMP()) -{warehouse_filter} -GROUP BY warehouse_name, DATE_TRUNC('day', start_time) -ORDER BY usage_date DESC, credits_used DESC -LIMIT ? ` const SNOWFLAKE_CREDIT_SUMMARY_SQL = ` @@ -198,7 +194,8 @@ function buildCreditUsageSql( const whF = warehouseFilter ? (binds.push(warehouseFilter), "AND warehouse_name = ?") : "" binds.push(limit) return { - sql: SNOWFLAKE_CREDIT_USAGE_SQL.replace("{warehouse_filter}", whF), + sql: SNOWFLAKE_CREDIT_USAGE_SQL + + `${whF}\nGROUP BY warehouse_name, DATE_TRUNC('day', start_time)\nORDER BY usage_date DESC, credits_used DESC\nLIMIT ?\n`, binds, } } diff --git a/packages/opencode/src/altimate/native/finops/query-history.ts b/packages/opencode/src/altimate/native/finops/query-history.ts index a59241e83d..1bcfdcb4e9 100644 --- a/packages/opencode/src/altimate/native/finops/query-history.ts +++ b/packages/opencode/src/altimate/native/finops/query-history.ts @@ -33,10 +33,6 @@ SELECT credits_used_cloud_services FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY WHERE start_time >= DATEADD('day', ?, CURRENT_TIMESTAMP()) -{user_filter} -{warehouse_filter} -ORDER BY start_time DESC -LIMIT ? ` const POSTGRES_HISTORY_SQL = ` @@ -59,7 +55,7 @@ SELECT calls as execution_count FROM pg_stat_statements ORDER BY total_exec_time DESC -LIMIT {limit} +LIMIT ? ` const BIGQUERY_HISTORY_SQL = ` @@ -127,14 +123,13 @@ function buildHistoryQuery( const whF = warehouseFilter ? (binds.push(warehouseFilter), "AND warehouse_name = ?") : "" binds.push(limit) return { - sql: SNOWFLAKE_HISTORY_SQL - .replace("{user_filter}", userF) - .replace("{warehouse_filter}", whF), + sql: SNOWFLAKE_HISTORY_SQL + + `${userF}\n${whF}\nORDER BY start_time DESC\nLIMIT ?\n`, binds, } } if (whType === "postgres" || whType === "postgresql") { - return { sql: POSTGRES_HISTORY_SQL.replace("{limit}", String(Math.floor(Number(limit)))), binds: [] } + return { sql: POSTGRES_HISTORY_SQL, binds: [limit] } } if (whType === "bigquery") { return { sql: BIGQUERY_HISTORY_SQL, binds: [days, limit] } diff --git a/packages/opencode/src/altimate/native/finops/role-access.ts b/packages/opencode/src/altimate/native/finops/role-access.ts index 9df6cecd13..e3bddbdee7 100644 --- a/packages/opencode/src/altimate/native/finops/role-access.ts +++ b/packages/opencode/src/altimate/native/finops/role-access.ts @@ -29,11 +29,6 @@ SELECT created_on FROM SNOWFLAKE.ACCOUNT_USAGE.GRANTS_TO_ROLES WHERE 1=1 -{role_filter} -{object_filter} -AND deleted_on IS NULL -ORDER BY granted_on, name -LIMIT ? ` const SNOWFLAKE_ROLE_HIERARCHY_SQL = ` @@ -57,9 +52,6 @@ SELECT created_on FROM SNOWFLAKE.ACCOUNT_USAGE.GRANTS_TO_USERS WHERE deleted_on IS NULL -{user_filter} -ORDER BY grantee_name, role -LIMIT ? ` // --------------------------------------------------------------------------- @@ -77,9 +69,6 @@ SELECT '' as created_on FROM \`region-US.INFORMATION_SCHEMA.OBJECT_PRIVILEGES\` WHERE 1=1 -{grantee_filter} -ORDER BY object_type, object_name -LIMIT ? ` // --------------------------------------------------------------------------- @@ -97,9 +86,6 @@ SELECT '' as created_on FROM system.information_schema.table_privileges WHERE 1=1 -{grantee_filter} -ORDER BY table_name -LIMIT ? ` // --------------------------------------------------------------------------- @@ -131,9 +117,8 @@ function buildGrantsSql( const objF = objectName ? (binds.push(objectName), "AND name = ?") : "" binds.push(limit) return { - sql: SNOWFLAKE_GRANTS_ON_SQL - .replace("{role_filter}", roleF) - .replace("{object_filter}", objF), + sql: SNOWFLAKE_GRANTS_ON_SQL + + `${roleF}\n${objF}\nAND deleted_on IS NULL\nORDER BY granted_on, name\nLIMIT ?\n`, binds, } } @@ -142,7 +127,8 @@ function buildGrantsSql( const granteeF = role ? (binds.push(role), "AND grantee = ?") : "" binds.push(limit) return { - sql: BIGQUERY_GRANTS_SQL.replace("{grantee_filter}", granteeF), + sql: BIGQUERY_GRANTS_SQL + + `${granteeF}\nORDER BY object_type, object_name\nLIMIT ?\n`, binds, } } @@ -151,7 +137,8 @@ function buildGrantsSql( const granteeF = role ? (binds.push(role), "AND grantee = ?") : "" binds.push(limit) return { - sql: DATABRICKS_GRANTS_SQL.replace("{grantee_filter}", granteeF), + sql: DATABRICKS_GRANTS_SQL + + `${granteeF}\nORDER BY table_name\nLIMIT ?\n`, binds, } } @@ -263,7 +250,8 @@ export async function queryUserRoles(params: UserRolesParams): Promise= DATEADD('day', -{days}, CURRENT_TIMESTAMP()) +WHERE start_time >= DATEADD('day', ?, CURRENT_TIMESTAMP()) GROUP BY warehouse_name ORDER BY avg_queue_load DESC ` @@ -36,7 +36,7 @@ SELECT AVG(bytes_scanned) as avg_bytes_scanned, SUM(credits_used_cloud_services) as total_credits FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY -WHERE start_time >= DATEADD('day', -{days}, CURRENT_TIMESTAMP()) +WHERE start_time >= DATEADD('day', ?, CURRENT_TIMESTAMP()) AND execution_status = 'SUCCESS' GROUP BY warehouse_name ORDER BY total_credits DESC @@ -59,7 +59,7 @@ SELECT MAX(period_slot_ms / 1000.0) as peak_queue_load, COUNT(*) as sample_count FROM \`region-US.INFORMATION_SCHEMA.JOBS_TIMELINE\` -WHERE period_start >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL {days} DAY) +WHERE period_start >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL ? DAY) GROUP BY reservation_id ORDER BY avg_concurrency DESC ` @@ -74,7 +74,7 @@ SELECT AVG(total_bytes_billed) as avg_bytes_scanned, SUM(total_bytes_billed) / 1099511627776.0 * 5.0 as total_credits FROM \`region-US.INFORMATION_SCHEMA.JOBS\` -WHERE creation_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL {days} DAY) +WHERE creation_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL ? DAY) AND job_type = 'QUERY' AND state = 'DONE' GROUP BY reservation_id @@ -94,7 +94,7 @@ SELECT MAX(num_queued_queries) as peak_queue_load, COUNT(*) as sample_count FROM system.compute.warehouse_events -WHERE event_time >= DATE_SUB(CURRENT_DATE(), {days}) +WHERE event_time >= DATE_SUB(CURRENT_DATE(), ?) GROUP BY warehouse_id ORDER BY avg_queue_load DESC ` @@ -109,7 +109,7 @@ SELECT AVG(read_bytes) as avg_bytes_scanned, 0 as total_credits FROM system.query.history -WHERE start_time >= DATE_SUB(CURRENT_DATE(), {days}) +WHERE start_time >= DATE_SUB(CURRENT_DATE(), ?) AND status = 'FINISHED' GROUP BY warehouse_id ORDER BY query_count DESC @@ -127,17 +127,17 @@ function getWhType(warehouse: string): string { return wh?.type || "unknown" } -function buildLoadSql(whType: string, days: number): string | null { - if (whType === "snowflake") return SNOWFLAKE_LOAD_SQL.replace("{days}", String(days)) - if (whType === "bigquery") return BIGQUERY_LOAD_SQL.replace("{days}", String(days)) - if (whType === "databricks") return DATABRICKS_LOAD_SQL.replace(/{days}/g, String(days)) +function buildLoadSql(whType: string, days: number): { sql: string; binds: any[] } | null { + if (whType === "snowflake") return { sql: SNOWFLAKE_LOAD_SQL, binds: [-days] } + if (whType === "bigquery") return { sql: BIGQUERY_LOAD_SQL, binds: [days] } + if (whType === "databricks") return { sql: DATABRICKS_LOAD_SQL, binds: [days] } return null } -function buildSizingSql(whType: string, days: number): string | null { - if (whType === "snowflake") return SNOWFLAKE_SIZING_SQL.replace("{days}", String(days)) - if (whType === "bigquery") return BIGQUERY_SIZING_SQL.replace("{days}", String(days)) - if (whType === "databricks") return DATABRICKS_SIZING_SQL.replace(/{days}/g, String(days)) +function buildSizingSql(whType: string, days: number): { sql: string; binds: any[] } | null { + if (whType === "snowflake") return { sql: SNOWFLAKE_SIZING_SQL, binds: [-days] } + if (whType === "bigquery") return { sql: BIGQUERY_SIZING_SQL, binds: [days] } + if (whType === "databricks") return { sql: DATABRICKS_SIZING_SQL, binds: [days] } return null } @@ -218,10 +218,10 @@ export async function adviseWarehouse(params: WarehouseAdvisorParams): Promise { ? (binds.push(params.tag_name), "WHERE tag_name = ?") : "" binds.push(limit) - sql = SNOWFLAKE_TAG_REFERENCES_SQL.replace("{tag_filter}", tagFilter) + sql = SNOWFLAKE_TAG_REFERENCES_SQL + + `${tagFilter}\nORDER BY tag_name, object_name\nLIMIT ?\n` } else { // Fall back to listing all tags binds = [limit] diff --git a/packages/opencode/test/altimate/schema-finops-dbt.test.ts b/packages/opencode/test/altimate/schema-finops-dbt.test.ts index 8e6d59075e..566a302da5 100644 --- a/packages/opencode/test/altimate/schema-finops-dbt.test.ts +++ b/packages/opencode/test/altimate/schema-finops-dbt.test.ts @@ -141,7 +141,8 @@ describe("FinOps: SQL template generation", () => { test("builds PostgreSQL history SQL", () => { const built = HistoryTemplates.buildHistoryQuery("postgres", 7, 50) expect(built?.sql).toContain("pg_stat_statements") - expect(built?.sql).toContain("50") // postgres still uses string interpolation + expect(built?.sql).toContain("LIMIT ?") + expect(built?.binds).toEqual([50]) }) test("returns null for DuckDB (no query history)", () => { @@ -162,18 +163,21 @@ describe("FinOps: SQL template generation", () => { describe("warehouse-advisor", () => { test("builds Snowflake load SQL", () => { - const sql = AdvisorTemplates.buildLoadSql("snowflake", 14) - expect(sql).toContain("WAREHOUSE_LOAD_HISTORY") + const built = AdvisorTemplates.buildLoadSql("snowflake", 14) + expect(built?.sql).toContain("WAREHOUSE_LOAD_HISTORY") + expect(built?.binds).toEqual([-14]) }) test("builds Snowflake sizing SQL", () => { - const sql = AdvisorTemplates.buildSizingSql("snowflake", 14) - expect(sql).toContain("PERCENTILE_CONT") + const built = AdvisorTemplates.buildSizingSql("snowflake", 14) + expect(built?.sql).toContain("PERCENTILE_CONT") + expect(built?.binds).toEqual([-14]) }) test("builds BigQuery load SQL", () => { - const sql = AdvisorTemplates.buildLoadSql("bigquery", 14) - expect(sql).toContain("JOBS_TIMELINE") + const built = AdvisorTemplates.buildLoadSql("bigquery", 14) + expect(built?.sql).toContain("JOBS_TIMELINE") + expect(built?.binds).toEqual([14]) }) test("returns null for unsupported types", () => { From ca0d93c6b80bea61d8a4aff769bc1061392741c4 Mon Sep 17 00:00:00 2001 From: Vijay Yadav Date: Tue, 24 Mar 2026 00:28:31 -0400 Subject: [PATCH 2/3] fix: keep Postgres LIMIT as rendered value since driver lacks bind support The Postgres driver ignores the binds parameter, so LIMIT ? would be sent as literal text and fail. Render LIMIT inline for Postgres while other warehouses use parameterized binds. --- .../opencode/src/altimate/native/finops/query-history.ts | 4 ++-- packages/opencode/test/altimate/schema-finops-dbt.test.ts | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/altimate/native/finops/query-history.ts b/packages/opencode/src/altimate/native/finops/query-history.ts index 1bcfdcb4e9..20acdbaa75 100644 --- a/packages/opencode/src/altimate/native/finops/query-history.ts +++ b/packages/opencode/src/altimate/native/finops/query-history.ts @@ -55,7 +55,6 @@ SELECT calls as execution_count FROM pg_stat_statements ORDER BY total_exec_time DESC -LIMIT ? ` const BIGQUERY_HISTORY_SQL = ` @@ -129,7 +128,8 @@ function buildHistoryQuery( } } if (whType === "postgres" || whType === "postgresql") { - return { sql: POSTGRES_HISTORY_SQL, binds: [limit] } + // Postgres driver does not support parameterized binds — render LIMIT inline + return { sql: POSTGRES_HISTORY_SQL + `LIMIT ${Math.floor(Number(limit))}\n`, binds: [] } } if (whType === "bigquery") { return { sql: BIGQUERY_HISTORY_SQL, binds: [days, limit] } diff --git a/packages/opencode/test/altimate/schema-finops-dbt.test.ts b/packages/opencode/test/altimate/schema-finops-dbt.test.ts index 566a302da5..5813f6331e 100644 --- a/packages/opencode/test/altimate/schema-finops-dbt.test.ts +++ b/packages/opencode/test/altimate/schema-finops-dbt.test.ts @@ -141,8 +141,9 @@ describe("FinOps: SQL template generation", () => { test("builds PostgreSQL history SQL", () => { const built = HistoryTemplates.buildHistoryQuery("postgres", 7, 50) expect(built?.sql).toContain("pg_stat_statements") - expect(built?.sql).toContain("LIMIT ?") - expect(built?.binds).toEqual([50]) + // Postgres driver does not support binds — LIMIT is rendered inline + expect(built?.sql).toContain("LIMIT 50") + expect(built?.binds).toEqual([]) }) test("returns null for DuckDB (no query history)", () => { From 1ede45ff272fa2f140a52def4af968c00c0581cb Mon Sep 17 00:00:00 2001 From: Vijay Yadav Date: Sat, 28 Mar 2026 17:45:03 -0400 Subject: [PATCH 3/3] fix: keep SQL templates self-contained, use .replace() for filter placeholders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address review feedback on PR #432: 1. Restore GROUP BY, ORDER BY, and LIMIT clauses to SQL template constants so each template is a complete, readable query. Only the optional WHERE filter placeholders ({warehouse_filter}, {user_filter}, etc.) are replaced at the call site via .replace() — safe because they are hardcoded SQL fragments, not user input. 2. Fix incorrect comment in query-history.ts claiming Postgres driver does not support parameterized binds. The actual limitation is that our connector wrapper (packages/drivers/src/postgres.ts) does not yet pass the _binds parameter through to pg. LIMIT is still rendered inline via Math.floor(Number(limit)) until the driver is updated. 3. Double-newline issue from empty filters is resolved by the .replace() approach — replacing an empty placeholder leaves at most a blank line, which SQL ignores. Files: credit-analyzer.ts, query-history.ts, role-access.ts, tags.ts Co-Authored-By: Vijay Yadav --- .../altimate/native/finops/credit-analyzer.ts | 7 +++-- .../altimate/native/finops/query-history.ts | 19 ++++++++++--- .../src/altimate/native/finops/role-access.ts | 28 +++++++++++++------ .../src/altimate/native/schema/tags.ts | 6 ++-- .../test/altimate/schema-finops-dbt.test.ts | 2 +- 5 files changed, 45 insertions(+), 17 deletions(-) diff --git a/packages/opencode/src/altimate/native/finops/credit-analyzer.ts b/packages/opencode/src/altimate/native/finops/credit-analyzer.ts index f19b2bddbb..f8d22cc27b 100644 --- a/packages/opencode/src/altimate/native/finops/credit-analyzer.ts +++ b/packages/opencode/src/altimate/native/finops/credit-analyzer.ts @@ -27,6 +27,10 @@ SELECT AVG(credits_used) as avg_credits_per_query FROM SNOWFLAKE.ACCOUNT_USAGE.WAREHOUSE_METERING_HISTORY WHERE start_time >= DATEADD('day', ?, CURRENT_TIMESTAMP()) +{warehouse_filter} +GROUP BY warehouse_name, DATE_TRUNC('day', start_time) +ORDER BY usage_date DESC, credits_used DESC +LIMIT ? ` const SNOWFLAKE_CREDIT_SUMMARY_SQL = ` @@ -194,8 +198,7 @@ function buildCreditUsageSql( const whF = warehouseFilter ? (binds.push(warehouseFilter), "AND warehouse_name = ?") : "" binds.push(limit) return { - sql: SNOWFLAKE_CREDIT_USAGE_SQL + - `${whF}\nGROUP BY warehouse_name, DATE_TRUNC('day', start_time)\nORDER BY usage_date DESC, credits_used DESC\nLIMIT ?\n`, + sql: SNOWFLAKE_CREDIT_USAGE_SQL.replace("{warehouse_filter}", whF), binds, } } diff --git a/packages/opencode/src/altimate/native/finops/query-history.ts b/packages/opencode/src/altimate/native/finops/query-history.ts index 20acdbaa75..909b14fb9c 100644 --- a/packages/opencode/src/altimate/native/finops/query-history.ts +++ b/packages/opencode/src/altimate/native/finops/query-history.ts @@ -33,6 +33,10 @@ SELECT credits_used_cloud_services FROM SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY WHERE start_time >= DATEADD('day', ?, CURRENT_TIMESTAMP()) +{user_filter} +{warehouse_filter} +ORDER BY start_time DESC +LIMIT ? ` const POSTGRES_HISTORY_SQL = ` @@ -55,6 +59,7 @@ SELECT calls as execution_count FROM pg_stat_statements ORDER BY total_exec_time DESC +LIMIT {limit} ` const BIGQUERY_HISTORY_SQL = ` @@ -122,14 +127,20 @@ function buildHistoryQuery( const whF = warehouseFilter ? (binds.push(warehouseFilter), "AND warehouse_name = ?") : "" binds.push(limit) return { - sql: SNOWFLAKE_HISTORY_SQL + - `${userF}\n${whF}\nORDER BY start_time DESC\nLIMIT ?\n`, + sql: SNOWFLAKE_HISTORY_SQL + .replace("{user_filter}", userF) + .replace("{warehouse_filter}", whF), binds, } } if (whType === "postgres" || whType === "postgresql") { - // Postgres driver does not support parameterized binds — render LIMIT inline - return { sql: POSTGRES_HISTORY_SQL + `LIMIT ${Math.floor(Number(limit))}\n`, binds: [] } + // The Postgres connector (packages/drivers/src/postgres.ts) does not yet + // pass binds through to pg — its execute() ignores the _binds parameter. + // Render LIMIT inline with Math.floor to prevent injection. + return { + sql: POSTGRES_HISTORY_SQL.replace("{limit}", String(Math.floor(Number(limit)))), + binds: [], + } } if (whType === "bigquery") { return { sql: BIGQUERY_HISTORY_SQL, binds: [days, limit] } diff --git a/packages/opencode/src/altimate/native/finops/role-access.ts b/packages/opencode/src/altimate/native/finops/role-access.ts index e3bddbdee7..9df6cecd13 100644 --- a/packages/opencode/src/altimate/native/finops/role-access.ts +++ b/packages/opencode/src/altimate/native/finops/role-access.ts @@ -29,6 +29,11 @@ SELECT created_on FROM SNOWFLAKE.ACCOUNT_USAGE.GRANTS_TO_ROLES WHERE 1=1 +{role_filter} +{object_filter} +AND deleted_on IS NULL +ORDER BY granted_on, name +LIMIT ? ` const SNOWFLAKE_ROLE_HIERARCHY_SQL = ` @@ -52,6 +57,9 @@ SELECT created_on FROM SNOWFLAKE.ACCOUNT_USAGE.GRANTS_TO_USERS WHERE deleted_on IS NULL +{user_filter} +ORDER BY grantee_name, role +LIMIT ? ` // --------------------------------------------------------------------------- @@ -69,6 +77,9 @@ SELECT '' as created_on FROM \`region-US.INFORMATION_SCHEMA.OBJECT_PRIVILEGES\` WHERE 1=1 +{grantee_filter} +ORDER BY object_type, object_name +LIMIT ? ` // --------------------------------------------------------------------------- @@ -86,6 +97,9 @@ SELECT '' as created_on FROM system.information_schema.table_privileges WHERE 1=1 +{grantee_filter} +ORDER BY table_name +LIMIT ? ` // --------------------------------------------------------------------------- @@ -117,8 +131,9 @@ function buildGrantsSql( const objF = objectName ? (binds.push(objectName), "AND name = ?") : "" binds.push(limit) return { - sql: SNOWFLAKE_GRANTS_ON_SQL + - `${roleF}\n${objF}\nAND deleted_on IS NULL\nORDER BY granted_on, name\nLIMIT ?\n`, + sql: SNOWFLAKE_GRANTS_ON_SQL + .replace("{role_filter}", roleF) + .replace("{object_filter}", objF), binds, } } @@ -127,8 +142,7 @@ function buildGrantsSql( const granteeF = role ? (binds.push(role), "AND grantee = ?") : "" binds.push(limit) return { - sql: BIGQUERY_GRANTS_SQL + - `${granteeF}\nORDER BY object_type, object_name\nLIMIT ?\n`, + sql: BIGQUERY_GRANTS_SQL.replace("{grantee_filter}", granteeF), binds, } } @@ -137,8 +151,7 @@ function buildGrantsSql( const granteeF = role ? (binds.push(role), "AND grantee = ?") : "" binds.push(limit) return { - sql: DATABRICKS_GRANTS_SQL + - `${granteeF}\nORDER BY table_name\nLIMIT ?\n`, + sql: DATABRICKS_GRANTS_SQL.replace("{grantee_filter}", granteeF), binds, } } @@ -250,8 +263,7 @@ export async function queryUserRoles(params: UserRolesParams): Promise { ? (binds.push(params.tag_name), "WHERE tag_name = ?") : "" binds.push(limit) - sql = SNOWFLAKE_TAG_REFERENCES_SQL + - `${tagFilter}\nORDER BY tag_name, object_name\nLIMIT ?\n` + sql = SNOWFLAKE_TAG_REFERENCES_SQL.replace("{tag_filter}", tagFilter) } else { // Fall back to listing all tags binds = [limit] diff --git a/packages/opencode/test/altimate/schema-finops-dbt.test.ts b/packages/opencode/test/altimate/schema-finops-dbt.test.ts index 5813f6331e..37c7e55043 100644 --- a/packages/opencode/test/altimate/schema-finops-dbt.test.ts +++ b/packages/opencode/test/altimate/schema-finops-dbt.test.ts @@ -141,7 +141,7 @@ describe("FinOps: SQL template generation", () => { test("builds PostgreSQL history SQL", () => { const built = HistoryTemplates.buildHistoryQuery("postgres", 7, 50) expect(built?.sql).toContain("pg_stat_statements") - // Postgres driver does not support binds — LIMIT is rendered inline + // Postgres connector does not yet pass binds — LIMIT rendered inline expect(built?.sql).toContain("LIMIT 50") expect(built?.binds).toEqual([]) })