@@ -2,7 +2,13 @@ import { describe, it, expect, beforeEach } from "vitest";
22import { parseTSQLSelect , parseTSQLExpr , compileTSQL } from "../index.js" ;
33import { ClickHousePrinter , printToClickHouse , type PrintResult } from "./printer.js" ;
44import { createPrinterContext , PrinterContext } from "./printer_context.js" ;
5- import { createSchemaRegistry , column , type TableSchema , type SchemaRegistry } from "./schema.js" ;
5+ import {
6+ createSchemaRegistry ,
7+ column ,
8+ type TableSchema ,
9+ type SchemaRegistry ,
10+ } from "./schema.js" ;
11+ import type { BucketThreshold } from "./time_buckets.js" ;
612import { QueryError , SyntaxError } from "./errors.js" ;
713
814/**
@@ -3570,4 +3576,73 @@ describe("timeBucket()", () => {
35703576 expect ( Object . values ( params ) ) . toContain ( "org_test123" ) ;
35713577 } ) ;
35723578 } ) ;
3579+
3580+ describe ( "per-table timeBucketThresholds" , ( ) => {
3581+ const customThresholds : BucketThreshold [ ] = [
3582+ // 10-second minimum granularity (e.g., for pre-aggregated metrics)
3583+ { maxRangeSeconds : 10 * 60 , interval : { value : 10 , unit : "SECOND" } } ,
3584+ { maxRangeSeconds : 30 * 60 , interval : { value : 30 , unit : "SECOND" } } ,
3585+ { maxRangeSeconds : 2 * 60 * 60 , interval : { value : 1 , unit : "MINUTE" } } ,
3586+ ] ;
3587+
3588+ const schemaWithCustomThresholds : TableSchema = {
3589+ ...timeBucketSchema ,
3590+ name : "metrics" ,
3591+ timeBucketThresholds : customThresholds ,
3592+ } ;
3593+
3594+ it ( "should use custom thresholds when defined on the table schema" , ( ) => {
3595+ // 3-minute range: global default would give 5 SECOND, custom gives 10 SECOND
3596+ const threeMinuteRange = {
3597+ from : new Date ( "2024-01-01T00:00:00Z" ) ,
3598+ to : new Date ( "2024-01-01T00:03:00Z" ) ,
3599+ } ;
3600+
3601+ const schema = createSchemaRegistry ( [ schemaWithCustomThresholds ] ) ;
3602+ const ctx = createPrinterContext ( {
3603+ schema,
3604+ enforcedWhereClause : {
3605+ organization_id : { op : "eq" , value : "org_test123" } ,
3606+ project_id : { op : "eq" , value : "proj_test456" } ,
3607+ environment_id : { op : "eq" , value : "env_test789" } ,
3608+ } ,
3609+ timeRange : threeMinuteRange ,
3610+ } ) ;
3611+
3612+ const ast = parseTSQLSelect (
3613+ "SELECT timeBucket(), count() FROM metrics GROUP BY timeBucket"
3614+ ) ;
3615+ const { sql } = printToClickHouse ( ast , ctx ) ;
3616+
3617+ // Custom thresholds: under 10 min → 10 SECOND (not the global 5 SECOND)
3618+ expect ( sql ) . toContain ( "toStartOfInterval(created_at, INTERVAL 10 SECOND)" ) ;
3619+ } ) ;
3620+
3621+ it ( "should fall back to global defaults when no custom thresholds are defined" , ( ) => {
3622+ // 3-minute range with standard schema (no custom thresholds)
3623+ const threeMinuteRange = {
3624+ from : new Date ( "2024-01-01T00:00:00Z" ) ,
3625+ to : new Date ( "2024-01-01T00:03:00Z" ) ,
3626+ } ;
3627+
3628+ const schema = createSchemaRegistry ( [ timeBucketSchema ] ) ;
3629+ const ctx = createPrinterContext ( {
3630+ schema,
3631+ enforcedWhereClause : {
3632+ organization_id : { op : "eq" , value : "org_test123" } ,
3633+ project_id : { op : "eq" , value : "proj_test456" } ,
3634+ environment_id : { op : "eq" , value : "env_test789" } ,
3635+ } ,
3636+ timeRange : threeMinuteRange ,
3637+ } ) ;
3638+
3639+ const ast = parseTSQLSelect (
3640+ "SELECT timeBucket(), count() FROM runs GROUP BY timeBucket"
3641+ ) ;
3642+ const { sql } = printToClickHouse ( ast , ctx ) ;
3643+
3644+ // Global default: under 5 min → 5 SECOND
3645+ expect ( sql ) . toContain ( "toStartOfInterval(created_at, INTERVAL 5 SECOND)" ) ;
3646+ } ) ;
3647+ } ) ;
35733648} ) ;
0 commit comments