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..20acdbaa75 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,6 @@ SELECT calls as execution_count FROM pg_stat_statements ORDER BY total_exec_time DESC -LIMIT {limit} ` const BIGQUERY_HISTORY_SQL = ` @@ -127,14 +122,14 @@ 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: [] } + // 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/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..5813f6331e 100644 --- a/packages/opencode/test/altimate/schema-finops-dbt.test.ts +++ b/packages/opencode/test/altimate/schema-finops-dbt.test.ts @@ -141,7 +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("50") // postgres still uses string interpolation + // 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)", () => { @@ -162,18 +164,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", () => {