88import { getMetadata } from '@/metadata' ;
99
1010import {
11+ getSourceTableColumn ,
1112 inferMaterializedViewConfig ,
1213 inferTimestampColumnGranularity ,
1314 parseSummedColumns ,
@@ -87,20 +88,24 @@ describe('inferMaterializedViewConfig', () => {
8788 name : 'SpanKind' ,
8889 type : 'LowCardinality(String)' ,
8990 } ) ,
91+ createMockColumnMeta ( {
92+ name : 'quantileDuration' ,
93+ type : 'AggregateFunction(quantile(0.5), UInt64)' ,
94+ } ) ,
9095 createMockColumnMeta ( {
9196 name : 'count' ,
9297 type : 'UInt64' ,
9398 } ) ,
9499 createMockColumnMeta ( {
95- name : 'sum__Duration ' ,
100+ name : 'sumDuration ' ,
96101 type : 'UInt64' ,
97102 } ) ,
98103 ] as ColumnMeta [ ] ,
99104 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
100105 meta : {
101106 engine : 'SummingMergeTree' ,
102107 engine_full :
103- 'SummingMergeTree((count, sum__Duration )) ORDER BY (Timestamp, ServiceName, SpanKind) SETTINGS index_granularity = 8192' ,
108+ 'SummingMergeTree((count, sumDuration )) ORDER BY (Timestamp, ServiceName, SpanKind) SETTINGS index_granularity = 8192' ,
104109 database : 'test_db' ,
105110 name : 'test_mv_target_table_summing' ,
106111 primary_key : 'Timestamp, ServiceName, SpanKind' ,
@@ -131,11 +136,27 @@ describe('inferMaterializedViewConfig', () => {
131136 name : 'test_mv' ,
132137 create_table_query : `CREATE MATERIALIZED VIEW test_db.test_mv TO test_db.test_mv_target_table AS
133138 SELECT toStartOfHour(Timestamp) AS Timestamp, ServiceName, SpanKind,
134- count(*) AS count, sum(Duration) AS sum__Duration, quantileState(0.5)(Duration) AS quantile__Duration
139+ count(*) AS count, sum(Duration) AS sum__Duration, histogram(20)(Duration) as histogram__Duration, quantileState(0.5)(Duration) AS quantile__Duration
135140 FROM test_source_table
136141 GROUP BY Timestamp, ServiceName, SpanKind` ,
137142 as_select : `SELECT toStartOfHour(Timestamp) AS Timestamp, ServiceName, SpanKind,
138- count(*) AS count, sum(Duration) AS sum__Duration, quantileState(0.5)(Duration) AS quantile__Duration
143+ count(*) AS count, sum(Duration) AS sum__Duration, histogram(20)(Duration) as histogram__Duration, quantileState(0.5)(Duration) AS quantile__Duration
144+ FROM test_source_table
145+ GROUP BY Timestamp, ServiceName, SpanKind` ,
146+ } as unknown as TableMetadata ,
147+ } ;
148+
149+ const summingMergeTreeMV = {
150+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
151+ meta : {
152+ engine : 'MaterializedView' ,
153+ database : 'test_db' ,
154+ name : 'test_mv_summing' ,
155+ create_table_query : `CREATE MATERIALIZED VIEW test_db.test_mv_summing TO test_db.test_mv_target_table_summing AS
156+ SELECT toStartOfHour(Timestamp) AS Timestamp, ServiceName, SpanKind, count() AS count, sum(Duration) AS sumDuration, quantileState(0.5)(Duration) AS quantileDuration
157+ FROM test_source_table
158+ GROUP BY Timestamp, ServiceName, SpanKind` ,
159+ as_select : `SELECT toStartOfHour(Timestamp) AS Timestamp, ServiceName, SpanKind, count() AS count, sum(Duration) AS sumDuration, quantileState(0.5)(Duration) AS quantileDuration
139160 FROM test_source_table
140161 GROUP BY Timestamp, ServiceName, SpanKind` ,
141162 } as unknown as TableMetadata ,
@@ -148,6 +169,8 @@ describe('inferMaterializedViewConfig', () => {
148169 . mockImplementation ( ( { tableName } ) => {
149170 if ( tableName === 'test_mv' ) {
150171 return Promise . resolve ( mv . meta ) ;
172+ } else if ( tableName === 'test_mv_summing' ) {
173+ return Promise . resolve ( summingMergeTreeMV . meta ) ;
151174 } else if ( tableName === 'test_mv_target_table' ) {
152175 return Promise . resolve ( mvTargetTable . meta ) ;
153176 } else if ( tableName === 'test_mv_target_table_summing' ) {
@@ -170,7 +193,17 @@ describe('inferMaterializedViewConfig', () => {
170193
171194 mockMetadata . queryMaterializedViewsByTarget = jest
172195 . fn ( )
173- . mockResolvedValue ( [ { tableName : 'test_mv' , databaseName : 'test_db' } ] ) ;
196+ . mockImplementation ( ( { tableName } ) => {
197+ return Promise . resolve ( [
198+ {
199+ databaseName : 'test_db' ,
200+ tableName :
201+ tableName === 'test_mv_target_table_summing'
202+ ? 'test_mv_summing'
203+ : 'test_mv' ,
204+ } ,
205+ ] ) ;
206+ } ) ;
174207 } ) ;
175208
176209 afterEach ( ( ) => {
@@ -300,14 +333,19 @@ describe('inferMaterializedViewConfig', () => {
300333 timestampColumn : 'Timestamp' ,
301334 minGranularity : '1 hour' ,
302335 aggregatedColumns : [
336+ {
337+ aggFn : 'quantile' ,
338+ mvColumn : 'quantileDuration' ,
339+ sourceColumn : 'Duration' ,
340+ } ,
303341 {
304342 aggFn : 'count' ,
305343 mvColumn : 'count' ,
306344 sourceColumn : '' ,
307345 } ,
308346 {
309347 aggFn : 'sum' ,
310- mvColumn : 'sum__Duration ' ,
348+ mvColumn : 'sumDuration ' ,
311349 sourceColumn : 'Duration' ,
312350 } ,
313351 ] ,
@@ -533,3 +571,128 @@ describe('parseSummedColumns', () => {
533571 expect ( parsed ) . toEqual ( new Set ( [ 'count' , 'sum__Duration' ] ) ) ;
534572 } ) ;
535573} ) ;
574+
575+ describe ( 'getSourceTableColumn' , ( ) => {
576+ it ( 'should return empty string if no matching source column is found' , ( ) => {
577+ const sourceTableColumns : ColumnMeta [ ] = [
578+ createMockColumnMeta ( { name : 'Duration' , type : 'UInt64' } ) ,
579+ createMockColumnMeta ( { name : 'Value' , type : 'UInt64' } ) ,
580+ ] ;
581+
582+ const targetTableColumn = createMockColumnMeta ( {
583+ name : 'sum__NonExistentColumn' ,
584+ type : 'SimpleAggregateFunction(sum, UInt64)' ,
585+ } ) ;
586+ const sourceColumn = getSourceTableColumn (
587+ 'sum' ,
588+ targetTableColumn ,
589+ sourceTableColumns ,
590+ ) ;
591+ expect ( sourceColumn ) . toBe ( '' ) ;
592+ } ) ;
593+
594+ it ( 'should return empty string if the aggFn is count' , ( ) => {
595+ const sourceTableColumns : ColumnMeta [ ] = [
596+ createMockColumnMeta ( { name : 'Duration' , type : 'UInt64' } ) ,
597+ createMockColumnMeta ( { name : 'Value' , type : 'UInt64' } ) ,
598+ ] ;
599+
600+ const targetTableColumn = createMockColumnMeta ( {
601+ name : 'count' ,
602+ type : 'SimpleAggregateFunction(count, UInt64)' ,
603+ } ) ;
604+ const sourceColumn = getSourceTableColumn (
605+ 'count' ,
606+ targetTableColumn ,
607+ sourceTableColumns ,
608+ ) ;
609+ expect ( sourceColumn ) . toBe ( '' ) ;
610+ } ) ;
611+
612+ it ( 'should match source columns based on convention' , ( ) => {
613+ const sourceTableColumns : ColumnMeta [ ] = [
614+ createMockColumnMeta ( { name : 'Duration' , type : 'UInt64' } ) ,
615+ createMockColumnMeta ( { name : 'Value' , type : 'UInt64' } ) ,
616+ ] ;
617+
618+ const targetTableColumnSum = createMockColumnMeta ( {
619+ name : 'sum__Duration' ,
620+ type : 'SimpleAggregateFunction(sum, UInt64)' ,
621+ } ) ;
622+ const sourceColumnForSum = getSourceTableColumn (
623+ 'sum' ,
624+ targetTableColumnSum ,
625+ sourceTableColumns ,
626+ ) ;
627+ expect ( sourceColumnForSum ) . toBe ( 'Duration' ) ;
628+ } ) ;
629+
630+ it ( 'should match source column based on MV DDL expressions' , ( ) => {
631+ const sourceTableColumns : ColumnMeta [ ] = [
632+ createMockColumnMeta ( { name : 'Duration' , type : 'UInt64' } ) ,
633+ createMockColumnMeta ( { name : 'Value' , type : 'UInt64' } ) ,
634+ ] ;
635+
636+ const targetTableColumnQuantile = createMockColumnMeta ( {
637+ name : 'quantileDuration' ,
638+ type : 'AggregateFunction(quantile(0.5), UInt64)' ,
639+ } ) ;
640+
641+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
642+ const mvMetadata : TableMetadata = {
643+ as_select :
644+ 'SELECT toStartOfHour(Timestamp) AS Timestamp, ServiceName, SpanKind, count() AS count, sum(Duration) AS sumDuration, quantileState(0.5)(Duration) AS quantileDuration FROM test_source_table GROUP BY Timestamp, ServiceName, SpanKind' ,
645+ } as unknown as TableMetadata ;
646+
647+ const sourceColumnForQuantile = getSourceTableColumn (
648+ 'quantile' ,
649+ targetTableColumnQuantile ,
650+ sourceTableColumns ,
651+ mvMetadata ,
652+ ) ;
653+
654+ expect ( sourceColumnForQuantile ) . toBe ( 'Duration' ) ;
655+ } ) ;
656+
657+ it ( 'should match source column based on MV DDL expressions when there are overlapping source column names' , ( ) => {
658+ const sourceTableColumns : ColumnMeta [ ] = [
659+ createMockColumnMeta ( { name : 'MaxDuration' , type : 'UInt64' } ) ,
660+ createMockColumnMeta ( { name : 'Duration' , type : 'UInt64' } ) ,
661+ createMockColumnMeta ( { name : 'Value' , type : 'UInt64' } ) ,
662+ ] ;
663+
664+ const targetTableColumnMaxMax = createMockColumnMeta ( {
665+ name : 'maxMaxDuration' ,
666+ type : 'AggregateFunction(max, UInt64)' ,
667+ } ) ;
668+
669+ const targetTableColumnMax = createMockColumnMeta ( {
670+ name : 'maxDuration' ,
671+ type : 'AggregateFunction(max, UInt64)' ,
672+ } ) ;
673+
674+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
675+ const mvMetadata : TableMetadata = {
676+ as_select :
677+ 'SELECT toStartOfHour(Timestamp) AS Timestamp, ServiceName, SpanKind, count() AS count, max(MaxDuration) AS maxMaxDuration, max(Duration) AS maxDuration FROM test_source_table GROUP BY Timestamp, ServiceName, SpanKind' ,
678+ } as unknown as TableMetadata ;
679+
680+ const sourceColumnForMax = getSourceTableColumn (
681+ 'max' ,
682+ targetTableColumnMax ,
683+ sourceTableColumns ,
684+ mvMetadata ,
685+ ) ;
686+
687+ expect ( sourceColumnForMax ) . toBe ( 'Duration' ) ;
688+
689+ const sourceColumnForMaxMax = getSourceTableColumn (
690+ 'max' ,
691+ targetTableColumnMaxMax ,
692+ sourceTableColumns ,
693+ mvMetadata ,
694+ ) ;
695+
696+ expect ( sourceColumnForMaxMax ) . toBe ( 'MaxDuration' ) ;
697+ } ) ;
698+ } ) ;
0 commit comments