diff --git a/grammar/FilterQuery.g4 b/grammar/FilterQuery.g4 index a87e399232..507689754a 100644 --- a/grammar/FilterQuery.g4 +++ b/grammar/FilterQuery.g4 @@ -39,6 +39,7 @@ primary | functionCall | fullText | key + | value ; /* @@ -189,9 +190,13 @@ BOOL | [Ff][Aa][Ll][Ss][Ee] ; -// Numbers (integer or float). Adjust as needed for your domain. +fragment SIGN : [+-] ; + +// Numbers: optional sign, then digits, optional fractional part, +// optional scientific notation (handy for future use) NUMBER - : DIGIT+ ( '.' DIGIT+ )? + : SIGN? DIGIT+ ('.' DIGIT*)? ([eE] SIGN? DIGIT+)? // -10.25 42 +3.14 6.02e23 + | SIGN? '.' DIGIT+ ([eE] SIGN? DIGIT+)? // -.75 .5 -.5e-3 ; // Double/single-quoted text, capturing full text search strings, values, etc. @@ -201,10 +206,12 @@ QUOTED_TEXT ) ; -// Keys can have letters, digits, underscores, dots, and bracket pairs -// e.g. service.name, service.namespace, db.queries[].query_duration +fragment SEGMENT : [a-zA-Z] [a-zA-Z0-9_:\-]* ; +fragment EMPTY_BRACKS : '[' ']' ; +fragment OLD_JSON_BRACKS: '[' '*' ']'; + KEY - : [a-zA-Z0-9_] [a-zA-Z0-9_.*[\]]* + : SEGMENT ( '.' SEGMENT | EMPTY_BRACKS | OLD_JSON_BRACKS)* ; // Ignore whitespace diff --git a/pkg/parser/grammar/FilterQuery.interp b/pkg/parser/grammar/FilterQuery.interp index 154786c76c..b78a05c21a 100644 --- a/pkg/parser/grammar/FilterQuery.interp +++ b/pkg/parser/grammar/FilterQuery.interp @@ -91,4 +91,4 @@ key atn: -[4, 1, 33, 212, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 5, 2, 43, 8, 2, 10, 2, 12, 2, 46, 9, 2, 1, 3, 1, 3, 1, 3, 1, 3, 5, 3, 52, 8, 3, 10, 3, 12, 3, 55, 9, 3, 1, 4, 3, 4, 58, 8, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 70, 8, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, 6, 148, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 3, 7, 160, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 174, 8, 8, 1, 9, 1, 9, 1, 9, 5, 9, 179, 8, 9, 10, 9, 12, 9, 182, 9, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 5, 12, 194, 8, 12, 10, 12, 12, 12, 197, 9, 12, 1, 13, 1, 13, 1, 13, 3, 13, 202, 8, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 0, 0, 17, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 0, 6, 1, 0, 7, 8, 2, 0, 13, 13, 15, 15, 2, 0, 14, 14, 16, 16, 2, 0, 30, 30, 33, 33, 1, 0, 25, 27, 1, 0, 28, 31, 225, 0, 34, 1, 0, 0, 0, 2, 37, 1, 0, 0, 0, 4, 39, 1, 0, 0, 0, 6, 47, 1, 0, 0, 0, 8, 57, 1, 0, 0, 0, 10, 69, 1, 0, 0, 0, 12, 147, 1, 0, 0, 0, 14, 159, 1, 0, 0, 0, 16, 173, 1, 0, 0, 0, 18, 175, 1, 0, 0, 0, 20, 183, 1, 0, 0, 0, 22, 185, 1, 0, 0, 0, 24, 190, 1, 0, 0, 0, 26, 201, 1, 0, 0, 0, 28, 203, 1, 0, 0, 0, 30, 207, 1, 0, 0, 0, 32, 209, 1, 0, 0, 0, 34, 35, 3, 2, 1, 0, 35, 36, 5, 0, 0, 1, 36, 1, 1, 0, 0, 0, 37, 38, 3, 4, 2, 0, 38, 3, 1, 0, 0, 0, 39, 44, 3, 6, 3, 0, 40, 41, 5, 24, 0, 0, 41, 43, 3, 6, 3, 0, 42, 40, 1, 0, 0, 0, 43, 46, 1, 0, 0, 0, 44, 42, 1, 0, 0, 0, 44, 45, 1, 0, 0, 0, 45, 5, 1, 0, 0, 0, 46, 44, 1, 0, 0, 0, 47, 53, 3, 8, 4, 0, 48, 49, 5, 23, 0, 0, 49, 52, 3, 8, 4, 0, 50, 52, 3, 8, 4, 0, 51, 48, 1, 0, 0, 0, 51, 50, 1, 0, 0, 0, 52, 55, 1, 0, 0, 0, 53, 51, 1, 0, 0, 0, 53, 54, 1, 0, 0, 0, 54, 7, 1, 0, 0, 0, 55, 53, 1, 0, 0, 0, 56, 58, 5, 22, 0, 0, 57, 56, 1, 0, 0, 0, 57, 58, 1, 0, 0, 0, 58, 59, 1, 0, 0, 0, 59, 60, 3, 10, 5, 0, 60, 9, 1, 0, 0, 0, 61, 62, 5, 1, 0, 0, 62, 63, 3, 4, 2, 0, 63, 64, 5, 2, 0, 0, 64, 70, 1, 0, 0, 0, 65, 70, 3, 12, 6, 0, 66, 70, 3, 22, 11, 0, 67, 70, 3, 20, 10, 0, 68, 70, 3, 32, 16, 0, 69, 61, 1, 0, 0, 0, 69, 65, 1, 0, 0, 0, 69, 66, 1, 0, 0, 0, 69, 67, 1, 0, 0, 0, 69, 68, 1, 0, 0, 0, 70, 11, 1, 0, 0, 0, 71, 72, 3, 32, 16, 0, 72, 73, 5, 6, 0, 0, 73, 74, 3, 30, 15, 0, 74, 148, 1, 0, 0, 0, 75, 76, 3, 32, 16, 0, 76, 77, 7, 0, 0, 0, 77, 78, 3, 30, 15, 0, 78, 148, 1, 0, 0, 0, 79, 80, 3, 32, 16, 0, 80, 81, 5, 9, 0, 0, 81, 82, 3, 30, 15, 0, 82, 148, 1, 0, 0, 0, 83, 84, 3, 32, 16, 0, 84, 85, 5, 10, 0, 0, 85, 86, 3, 30, 15, 0, 86, 148, 1, 0, 0, 0, 87, 88, 3, 32, 16, 0, 88, 89, 5, 11, 0, 0, 89, 90, 3, 30, 15, 0, 90, 148, 1, 0, 0, 0, 91, 92, 3, 32, 16, 0, 92, 93, 5, 12, 0, 0, 93, 94, 3, 30, 15, 0, 94, 148, 1, 0, 0, 0, 95, 96, 3, 32, 16, 0, 96, 97, 7, 1, 0, 0, 97, 98, 3, 30, 15, 0, 98, 148, 1, 0, 0, 0, 99, 100, 3, 32, 16, 0, 100, 101, 7, 2, 0, 0, 101, 102, 3, 30, 15, 0, 102, 148, 1, 0, 0, 0, 103, 104, 3, 32, 16, 0, 104, 105, 5, 17, 0, 0, 105, 106, 3, 30, 15, 0, 106, 107, 5, 23, 0, 0, 107, 108, 3, 30, 15, 0, 108, 148, 1, 0, 0, 0, 109, 110, 3, 32, 16, 0, 110, 111, 5, 22, 0, 0, 111, 112, 5, 17, 0, 0, 112, 113, 3, 30, 15, 0, 113, 114, 5, 23, 0, 0, 114, 115, 3, 30, 15, 0, 115, 148, 1, 0, 0, 0, 116, 117, 3, 32, 16, 0, 117, 118, 3, 14, 7, 0, 118, 148, 1, 0, 0, 0, 119, 120, 3, 32, 16, 0, 120, 121, 3, 16, 8, 0, 121, 148, 1, 0, 0, 0, 122, 123, 3, 32, 16, 0, 123, 124, 5, 18, 0, 0, 124, 148, 1, 0, 0, 0, 125, 126, 3, 32, 16, 0, 126, 127, 5, 22, 0, 0, 127, 128, 5, 18, 0, 0, 128, 148, 1, 0, 0, 0, 129, 130, 3, 32, 16, 0, 130, 131, 5, 19, 0, 0, 131, 132, 3, 30, 15, 0, 132, 148, 1, 0, 0, 0, 133, 134, 3, 32, 16, 0, 134, 135, 5, 22, 0, 0, 135, 136, 5, 19, 0, 0, 136, 137, 3, 30, 15, 0, 137, 148, 1, 0, 0, 0, 138, 139, 3, 32, 16, 0, 139, 140, 5, 20, 0, 0, 140, 141, 3, 30, 15, 0, 141, 148, 1, 0, 0, 0, 142, 143, 3, 32, 16, 0, 143, 144, 5, 22, 0, 0, 144, 145, 5, 20, 0, 0, 145, 146, 3, 30, 15, 0, 146, 148, 1, 0, 0, 0, 147, 71, 1, 0, 0, 0, 147, 75, 1, 0, 0, 0, 147, 79, 1, 0, 0, 0, 147, 83, 1, 0, 0, 0, 147, 87, 1, 0, 0, 0, 147, 91, 1, 0, 0, 0, 147, 95, 1, 0, 0, 0, 147, 99, 1, 0, 0, 0, 147, 103, 1, 0, 0, 0, 147, 109, 1, 0, 0, 0, 147, 116, 1, 0, 0, 0, 147, 119, 1, 0, 0, 0, 147, 122, 1, 0, 0, 0, 147, 125, 1, 0, 0, 0, 147, 129, 1, 0, 0, 0, 147, 133, 1, 0, 0, 0, 147, 138, 1, 0, 0, 0, 147, 142, 1, 0, 0, 0, 148, 13, 1, 0, 0, 0, 149, 150, 5, 21, 0, 0, 150, 151, 5, 1, 0, 0, 151, 152, 3, 18, 9, 0, 152, 153, 5, 2, 0, 0, 153, 160, 1, 0, 0, 0, 154, 155, 5, 21, 0, 0, 155, 156, 5, 3, 0, 0, 156, 157, 3, 18, 9, 0, 157, 158, 5, 4, 0, 0, 158, 160, 1, 0, 0, 0, 159, 149, 1, 0, 0, 0, 159, 154, 1, 0, 0, 0, 160, 15, 1, 0, 0, 0, 161, 162, 5, 22, 0, 0, 162, 163, 5, 21, 0, 0, 163, 164, 5, 1, 0, 0, 164, 165, 3, 18, 9, 0, 165, 166, 5, 2, 0, 0, 166, 174, 1, 0, 0, 0, 167, 168, 5, 22, 0, 0, 168, 169, 5, 21, 0, 0, 169, 170, 5, 3, 0, 0, 170, 171, 3, 18, 9, 0, 171, 172, 5, 4, 0, 0, 172, 174, 1, 0, 0, 0, 173, 161, 1, 0, 0, 0, 173, 167, 1, 0, 0, 0, 174, 17, 1, 0, 0, 0, 175, 180, 3, 30, 15, 0, 176, 177, 5, 5, 0, 0, 177, 179, 3, 30, 15, 0, 178, 176, 1, 0, 0, 0, 179, 182, 1, 0, 0, 0, 180, 178, 1, 0, 0, 0, 180, 181, 1, 0, 0, 0, 181, 19, 1, 0, 0, 0, 182, 180, 1, 0, 0, 0, 183, 184, 7, 3, 0, 0, 184, 21, 1, 0, 0, 0, 185, 186, 7, 4, 0, 0, 186, 187, 5, 1, 0, 0, 187, 188, 3, 24, 12, 0, 188, 189, 5, 2, 0, 0, 189, 23, 1, 0, 0, 0, 190, 195, 3, 26, 13, 0, 191, 192, 5, 5, 0, 0, 192, 194, 3, 26, 13, 0, 193, 191, 1, 0, 0, 0, 194, 197, 1, 0, 0, 0, 195, 193, 1, 0, 0, 0, 195, 196, 1, 0, 0, 0, 196, 25, 1, 0, 0, 0, 197, 195, 1, 0, 0, 0, 198, 202, 3, 32, 16, 0, 199, 202, 3, 30, 15, 0, 200, 202, 3, 28, 14, 0, 201, 198, 1, 0, 0, 0, 201, 199, 1, 0, 0, 0, 201, 200, 1, 0, 0, 0, 202, 27, 1, 0, 0, 0, 203, 204, 5, 3, 0, 0, 204, 205, 3, 18, 9, 0, 205, 206, 5, 4, 0, 0, 206, 29, 1, 0, 0, 0, 207, 208, 7, 5, 0, 0, 208, 31, 1, 0, 0, 0, 209, 210, 5, 31, 0, 0, 210, 33, 1, 0, 0, 0, 11, 44, 51, 53, 57, 69, 147, 159, 173, 180, 195, 201] \ No newline at end of file +[4, 1, 33, 213, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 5, 2, 43, 8, 2, 10, 2, 12, 2, 46, 9, 2, 1, 3, 1, 3, 1, 3, 1, 3, 5, 3, 52, 8, 3, 10, 3, 12, 3, 55, 9, 3, 1, 4, 3, 4, 58, 8, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 71, 8, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, 6, 149, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 3, 7, 161, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 175, 8, 8, 1, 9, 1, 9, 1, 9, 5, 9, 180, 8, 9, 10, 9, 12, 9, 183, 9, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 5, 12, 195, 8, 12, 10, 12, 12, 12, 198, 9, 12, 1, 13, 1, 13, 1, 13, 3, 13, 203, 8, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 0, 0, 17, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 0, 6, 1, 0, 7, 8, 2, 0, 13, 13, 15, 15, 2, 0, 14, 14, 16, 16, 2, 0, 30, 30, 33, 33, 1, 0, 25, 27, 1, 0, 28, 31, 227, 0, 34, 1, 0, 0, 0, 2, 37, 1, 0, 0, 0, 4, 39, 1, 0, 0, 0, 6, 47, 1, 0, 0, 0, 8, 57, 1, 0, 0, 0, 10, 70, 1, 0, 0, 0, 12, 148, 1, 0, 0, 0, 14, 160, 1, 0, 0, 0, 16, 174, 1, 0, 0, 0, 18, 176, 1, 0, 0, 0, 20, 184, 1, 0, 0, 0, 22, 186, 1, 0, 0, 0, 24, 191, 1, 0, 0, 0, 26, 202, 1, 0, 0, 0, 28, 204, 1, 0, 0, 0, 30, 208, 1, 0, 0, 0, 32, 210, 1, 0, 0, 0, 34, 35, 3, 2, 1, 0, 35, 36, 5, 0, 0, 1, 36, 1, 1, 0, 0, 0, 37, 38, 3, 4, 2, 0, 38, 3, 1, 0, 0, 0, 39, 44, 3, 6, 3, 0, 40, 41, 5, 24, 0, 0, 41, 43, 3, 6, 3, 0, 42, 40, 1, 0, 0, 0, 43, 46, 1, 0, 0, 0, 44, 42, 1, 0, 0, 0, 44, 45, 1, 0, 0, 0, 45, 5, 1, 0, 0, 0, 46, 44, 1, 0, 0, 0, 47, 53, 3, 8, 4, 0, 48, 49, 5, 23, 0, 0, 49, 52, 3, 8, 4, 0, 50, 52, 3, 8, 4, 0, 51, 48, 1, 0, 0, 0, 51, 50, 1, 0, 0, 0, 52, 55, 1, 0, 0, 0, 53, 51, 1, 0, 0, 0, 53, 54, 1, 0, 0, 0, 54, 7, 1, 0, 0, 0, 55, 53, 1, 0, 0, 0, 56, 58, 5, 22, 0, 0, 57, 56, 1, 0, 0, 0, 57, 58, 1, 0, 0, 0, 58, 59, 1, 0, 0, 0, 59, 60, 3, 10, 5, 0, 60, 9, 1, 0, 0, 0, 61, 62, 5, 1, 0, 0, 62, 63, 3, 4, 2, 0, 63, 64, 5, 2, 0, 0, 64, 71, 1, 0, 0, 0, 65, 71, 3, 12, 6, 0, 66, 71, 3, 22, 11, 0, 67, 71, 3, 20, 10, 0, 68, 71, 3, 32, 16, 0, 69, 71, 3, 30, 15, 0, 70, 61, 1, 0, 0, 0, 70, 65, 1, 0, 0, 0, 70, 66, 1, 0, 0, 0, 70, 67, 1, 0, 0, 0, 70, 68, 1, 0, 0, 0, 70, 69, 1, 0, 0, 0, 71, 11, 1, 0, 0, 0, 72, 73, 3, 32, 16, 0, 73, 74, 5, 6, 0, 0, 74, 75, 3, 30, 15, 0, 75, 149, 1, 0, 0, 0, 76, 77, 3, 32, 16, 0, 77, 78, 7, 0, 0, 0, 78, 79, 3, 30, 15, 0, 79, 149, 1, 0, 0, 0, 80, 81, 3, 32, 16, 0, 81, 82, 5, 9, 0, 0, 82, 83, 3, 30, 15, 0, 83, 149, 1, 0, 0, 0, 84, 85, 3, 32, 16, 0, 85, 86, 5, 10, 0, 0, 86, 87, 3, 30, 15, 0, 87, 149, 1, 0, 0, 0, 88, 89, 3, 32, 16, 0, 89, 90, 5, 11, 0, 0, 90, 91, 3, 30, 15, 0, 91, 149, 1, 0, 0, 0, 92, 93, 3, 32, 16, 0, 93, 94, 5, 12, 0, 0, 94, 95, 3, 30, 15, 0, 95, 149, 1, 0, 0, 0, 96, 97, 3, 32, 16, 0, 97, 98, 7, 1, 0, 0, 98, 99, 3, 30, 15, 0, 99, 149, 1, 0, 0, 0, 100, 101, 3, 32, 16, 0, 101, 102, 7, 2, 0, 0, 102, 103, 3, 30, 15, 0, 103, 149, 1, 0, 0, 0, 104, 105, 3, 32, 16, 0, 105, 106, 5, 17, 0, 0, 106, 107, 3, 30, 15, 0, 107, 108, 5, 23, 0, 0, 108, 109, 3, 30, 15, 0, 109, 149, 1, 0, 0, 0, 110, 111, 3, 32, 16, 0, 111, 112, 5, 22, 0, 0, 112, 113, 5, 17, 0, 0, 113, 114, 3, 30, 15, 0, 114, 115, 5, 23, 0, 0, 115, 116, 3, 30, 15, 0, 116, 149, 1, 0, 0, 0, 117, 118, 3, 32, 16, 0, 118, 119, 3, 14, 7, 0, 119, 149, 1, 0, 0, 0, 120, 121, 3, 32, 16, 0, 121, 122, 3, 16, 8, 0, 122, 149, 1, 0, 0, 0, 123, 124, 3, 32, 16, 0, 124, 125, 5, 18, 0, 0, 125, 149, 1, 0, 0, 0, 126, 127, 3, 32, 16, 0, 127, 128, 5, 22, 0, 0, 128, 129, 5, 18, 0, 0, 129, 149, 1, 0, 0, 0, 130, 131, 3, 32, 16, 0, 131, 132, 5, 19, 0, 0, 132, 133, 3, 30, 15, 0, 133, 149, 1, 0, 0, 0, 134, 135, 3, 32, 16, 0, 135, 136, 5, 22, 0, 0, 136, 137, 5, 19, 0, 0, 137, 138, 3, 30, 15, 0, 138, 149, 1, 0, 0, 0, 139, 140, 3, 32, 16, 0, 140, 141, 5, 20, 0, 0, 141, 142, 3, 30, 15, 0, 142, 149, 1, 0, 0, 0, 143, 144, 3, 32, 16, 0, 144, 145, 5, 22, 0, 0, 145, 146, 5, 20, 0, 0, 146, 147, 3, 30, 15, 0, 147, 149, 1, 0, 0, 0, 148, 72, 1, 0, 0, 0, 148, 76, 1, 0, 0, 0, 148, 80, 1, 0, 0, 0, 148, 84, 1, 0, 0, 0, 148, 88, 1, 0, 0, 0, 148, 92, 1, 0, 0, 0, 148, 96, 1, 0, 0, 0, 148, 100, 1, 0, 0, 0, 148, 104, 1, 0, 0, 0, 148, 110, 1, 0, 0, 0, 148, 117, 1, 0, 0, 0, 148, 120, 1, 0, 0, 0, 148, 123, 1, 0, 0, 0, 148, 126, 1, 0, 0, 0, 148, 130, 1, 0, 0, 0, 148, 134, 1, 0, 0, 0, 148, 139, 1, 0, 0, 0, 148, 143, 1, 0, 0, 0, 149, 13, 1, 0, 0, 0, 150, 151, 5, 21, 0, 0, 151, 152, 5, 1, 0, 0, 152, 153, 3, 18, 9, 0, 153, 154, 5, 2, 0, 0, 154, 161, 1, 0, 0, 0, 155, 156, 5, 21, 0, 0, 156, 157, 5, 3, 0, 0, 157, 158, 3, 18, 9, 0, 158, 159, 5, 4, 0, 0, 159, 161, 1, 0, 0, 0, 160, 150, 1, 0, 0, 0, 160, 155, 1, 0, 0, 0, 161, 15, 1, 0, 0, 0, 162, 163, 5, 22, 0, 0, 163, 164, 5, 21, 0, 0, 164, 165, 5, 1, 0, 0, 165, 166, 3, 18, 9, 0, 166, 167, 5, 2, 0, 0, 167, 175, 1, 0, 0, 0, 168, 169, 5, 22, 0, 0, 169, 170, 5, 21, 0, 0, 170, 171, 5, 3, 0, 0, 171, 172, 3, 18, 9, 0, 172, 173, 5, 4, 0, 0, 173, 175, 1, 0, 0, 0, 174, 162, 1, 0, 0, 0, 174, 168, 1, 0, 0, 0, 175, 17, 1, 0, 0, 0, 176, 181, 3, 30, 15, 0, 177, 178, 5, 5, 0, 0, 178, 180, 3, 30, 15, 0, 179, 177, 1, 0, 0, 0, 180, 183, 1, 0, 0, 0, 181, 179, 1, 0, 0, 0, 181, 182, 1, 0, 0, 0, 182, 19, 1, 0, 0, 0, 183, 181, 1, 0, 0, 0, 184, 185, 7, 3, 0, 0, 185, 21, 1, 0, 0, 0, 186, 187, 7, 4, 0, 0, 187, 188, 5, 1, 0, 0, 188, 189, 3, 24, 12, 0, 189, 190, 5, 2, 0, 0, 190, 23, 1, 0, 0, 0, 191, 196, 3, 26, 13, 0, 192, 193, 5, 5, 0, 0, 193, 195, 3, 26, 13, 0, 194, 192, 1, 0, 0, 0, 195, 198, 1, 0, 0, 0, 196, 194, 1, 0, 0, 0, 196, 197, 1, 0, 0, 0, 197, 25, 1, 0, 0, 0, 198, 196, 1, 0, 0, 0, 199, 203, 3, 32, 16, 0, 200, 203, 3, 30, 15, 0, 201, 203, 3, 28, 14, 0, 202, 199, 1, 0, 0, 0, 202, 200, 1, 0, 0, 0, 202, 201, 1, 0, 0, 0, 203, 27, 1, 0, 0, 0, 204, 205, 5, 3, 0, 0, 205, 206, 3, 18, 9, 0, 206, 207, 5, 4, 0, 0, 207, 29, 1, 0, 0, 0, 208, 209, 7, 5, 0, 0, 209, 31, 1, 0, 0, 0, 210, 211, 5, 31, 0, 0, 211, 33, 1, 0, 0, 0, 11, 44, 51, 53, 57, 70, 148, 160, 174, 181, 196, 202] \ No newline at end of file diff --git a/pkg/parser/grammar/FilterQueryLexer.interp b/pkg/parser/grammar/FilterQueryLexer.interp index 5a08c7b188..888c0b75ba 100644 --- a/pkg/parser/grammar/FilterQueryLexer.interp +++ b/pkg/parser/grammar/FilterQueryLexer.interp @@ -99,8 +99,12 @@ HAS HASANY HASALL BOOL +SIGN NUMBER QUOTED_TEXT +SEGMENT +EMPTY_BRACKS +OLD_JSON_BRACKS KEY WS DIGIT @@ -114,4 +118,4 @@ mode names: DEFAULT_MODE atn: -[4, 0, 33, 270, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 3, 5, 83, 8, 5, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 4, 13, 110, 8, 13, 11, 13, 12, 13, 111, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 4, 15, 129, 8, 15, 11, 15, 12, 15, 130, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 3, 17, 153, 8, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 3, 19, 170, 8, 19, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 3, 27, 213, 8, 27, 1, 28, 4, 28, 216, 8, 28, 11, 28, 12, 28, 217, 1, 28, 1, 28, 4, 28, 222, 8, 28, 11, 28, 12, 28, 223, 3, 28, 226, 8, 28, 1, 29, 1, 29, 1, 29, 1, 29, 5, 29, 232, 8, 29, 10, 29, 12, 29, 235, 9, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 5, 29, 242, 8, 29, 10, 29, 12, 29, 245, 9, 29, 1, 29, 3, 29, 248, 8, 29, 1, 30, 1, 30, 5, 30, 252, 8, 30, 10, 30, 12, 30, 255, 9, 30, 1, 31, 4, 31, 258, 8, 31, 11, 31, 12, 31, 259, 1, 31, 1, 31, 1, 32, 1, 32, 1, 33, 4, 33, 267, 8, 33, 11, 33, 12, 33, 268, 0, 0, 34, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 0, 67, 33, 1, 0, 29, 2, 0, 76, 76, 108, 108, 2, 0, 73, 73, 105, 105, 2, 0, 75, 75, 107, 107, 2, 0, 69, 69, 101, 101, 2, 0, 78, 78, 110, 110, 2, 0, 79, 79, 111, 111, 2, 0, 84, 84, 116, 116, 2, 0, 9, 9, 32, 32, 2, 0, 66, 66, 98, 98, 2, 0, 87, 87, 119, 119, 2, 0, 88, 88, 120, 120, 2, 0, 83, 83, 115, 115, 2, 0, 82, 82, 114, 114, 2, 0, 71, 71, 103, 103, 2, 0, 80, 80, 112, 112, 2, 0, 67, 67, 99, 99, 2, 0, 65, 65, 97, 97, 2, 0, 68, 68, 100, 100, 2, 0, 72, 72, 104, 104, 2, 0, 89, 89, 121, 121, 2, 0, 85, 85, 117, 117, 2, 0, 70, 70, 102, 102, 2, 0, 34, 34, 92, 92, 2, 0, 39, 39, 92, 92, 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 7, 0, 42, 42, 46, 46, 48, 57, 65, 91, 93, 93, 95, 95, 97, 122, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, 8, 0, 9, 10, 13, 13, 32, 34, 39, 41, 44, 44, 60, 62, 91, 91, 93, 93, 285, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 1, 69, 1, 0, 0, 0, 3, 71, 1, 0, 0, 0, 5, 73, 1, 0, 0, 0, 7, 75, 1, 0, 0, 0, 9, 77, 1, 0, 0, 0, 11, 82, 1, 0, 0, 0, 13, 84, 1, 0, 0, 0, 15, 87, 1, 0, 0, 0, 17, 90, 1, 0, 0, 0, 19, 92, 1, 0, 0, 0, 21, 95, 1, 0, 0, 0, 23, 97, 1, 0, 0, 0, 25, 100, 1, 0, 0, 0, 27, 105, 1, 0, 0, 0, 29, 118, 1, 0, 0, 0, 31, 124, 1, 0, 0, 0, 33, 138, 1, 0, 0, 0, 35, 146, 1, 0, 0, 0, 37, 154, 1, 0, 0, 0, 39, 161, 1, 0, 0, 0, 41, 171, 1, 0, 0, 0, 43, 174, 1, 0, 0, 0, 45, 178, 1, 0, 0, 0, 47, 182, 1, 0, 0, 0, 49, 185, 1, 0, 0, 0, 51, 189, 1, 0, 0, 0, 53, 196, 1, 0, 0, 0, 55, 212, 1, 0, 0, 0, 57, 215, 1, 0, 0, 0, 59, 247, 1, 0, 0, 0, 61, 249, 1, 0, 0, 0, 63, 257, 1, 0, 0, 0, 65, 263, 1, 0, 0, 0, 67, 266, 1, 0, 0, 0, 69, 70, 5, 40, 0, 0, 70, 2, 1, 0, 0, 0, 71, 72, 5, 41, 0, 0, 72, 4, 1, 0, 0, 0, 73, 74, 5, 91, 0, 0, 74, 6, 1, 0, 0, 0, 75, 76, 5, 93, 0, 0, 76, 8, 1, 0, 0, 0, 77, 78, 5, 44, 0, 0, 78, 10, 1, 0, 0, 0, 79, 83, 5, 61, 0, 0, 80, 81, 5, 61, 0, 0, 81, 83, 5, 61, 0, 0, 82, 79, 1, 0, 0, 0, 82, 80, 1, 0, 0, 0, 83, 12, 1, 0, 0, 0, 84, 85, 5, 33, 0, 0, 85, 86, 5, 61, 0, 0, 86, 14, 1, 0, 0, 0, 87, 88, 5, 60, 0, 0, 88, 89, 5, 62, 0, 0, 89, 16, 1, 0, 0, 0, 90, 91, 5, 60, 0, 0, 91, 18, 1, 0, 0, 0, 92, 93, 5, 60, 0, 0, 93, 94, 5, 61, 0, 0, 94, 20, 1, 0, 0, 0, 95, 96, 5, 62, 0, 0, 96, 22, 1, 0, 0, 0, 97, 98, 5, 62, 0, 0, 98, 99, 5, 61, 0, 0, 99, 24, 1, 0, 0, 0, 100, 101, 7, 0, 0, 0, 101, 102, 7, 1, 0, 0, 102, 103, 7, 2, 0, 0, 103, 104, 7, 3, 0, 0, 104, 26, 1, 0, 0, 0, 105, 106, 7, 4, 0, 0, 106, 107, 7, 5, 0, 0, 107, 109, 7, 6, 0, 0, 108, 110, 7, 7, 0, 0, 109, 108, 1, 0, 0, 0, 110, 111, 1, 0, 0, 0, 111, 109, 1, 0, 0, 0, 111, 112, 1, 0, 0, 0, 112, 113, 1, 0, 0, 0, 113, 114, 7, 0, 0, 0, 114, 115, 7, 1, 0, 0, 115, 116, 7, 2, 0, 0, 116, 117, 7, 3, 0, 0, 117, 28, 1, 0, 0, 0, 118, 119, 7, 1, 0, 0, 119, 120, 7, 0, 0, 0, 120, 121, 7, 1, 0, 0, 121, 122, 7, 2, 0, 0, 122, 123, 7, 3, 0, 0, 123, 30, 1, 0, 0, 0, 124, 125, 7, 4, 0, 0, 125, 126, 7, 5, 0, 0, 126, 128, 7, 6, 0, 0, 127, 129, 7, 7, 0, 0, 128, 127, 1, 0, 0, 0, 129, 130, 1, 0, 0, 0, 130, 128, 1, 0, 0, 0, 130, 131, 1, 0, 0, 0, 131, 132, 1, 0, 0, 0, 132, 133, 7, 1, 0, 0, 133, 134, 7, 0, 0, 0, 134, 135, 7, 1, 0, 0, 135, 136, 7, 2, 0, 0, 136, 137, 7, 3, 0, 0, 137, 32, 1, 0, 0, 0, 138, 139, 7, 8, 0, 0, 139, 140, 7, 3, 0, 0, 140, 141, 7, 6, 0, 0, 141, 142, 7, 9, 0, 0, 142, 143, 7, 3, 0, 0, 143, 144, 7, 3, 0, 0, 144, 145, 7, 4, 0, 0, 145, 34, 1, 0, 0, 0, 146, 147, 7, 3, 0, 0, 147, 148, 7, 10, 0, 0, 148, 149, 7, 1, 0, 0, 149, 150, 7, 11, 0, 0, 150, 152, 7, 6, 0, 0, 151, 153, 7, 11, 0, 0, 152, 151, 1, 0, 0, 0, 152, 153, 1, 0, 0, 0, 153, 36, 1, 0, 0, 0, 154, 155, 7, 12, 0, 0, 155, 156, 7, 3, 0, 0, 156, 157, 7, 13, 0, 0, 157, 158, 7, 3, 0, 0, 158, 159, 7, 10, 0, 0, 159, 160, 7, 14, 0, 0, 160, 38, 1, 0, 0, 0, 161, 162, 7, 15, 0, 0, 162, 163, 7, 5, 0, 0, 163, 164, 7, 4, 0, 0, 164, 165, 7, 6, 0, 0, 165, 166, 7, 16, 0, 0, 166, 167, 7, 1, 0, 0, 167, 169, 7, 4, 0, 0, 168, 170, 7, 11, 0, 0, 169, 168, 1, 0, 0, 0, 169, 170, 1, 0, 0, 0, 170, 40, 1, 0, 0, 0, 171, 172, 7, 1, 0, 0, 172, 173, 7, 4, 0, 0, 173, 42, 1, 0, 0, 0, 174, 175, 7, 4, 0, 0, 175, 176, 7, 5, 0, 0, 176, 177, 7, 6, 0, 0, 177, 44, 1, 0, 0, 0, 178, 179, 7, 16, 0, 0, 179, 180, 7, 4, 0, 0, 180, 181, 7, 17, 0, 0, 181, 46, 1, 0, 0, 0, 182, 183, 7, 5, 0, 0, 183, 184, 7, 12, 0, 0, 184, 48, 1, 0, 0, 0, 185, 186, 7, 18, 0, 0, 186, 187, 7, 16, 0, 0, 187, 188, 7, 11, 0, 0, 188, 50, 1, 0, 0, 0, 189, 190, 7, 18, 0, 0, 190, 191, 7, 16, 0, 0, 191, 192, 7, 11, 0, 0, 192, 193, 7, 16, 0, 0, 193, 194, 7, 4, 0, 0, 194, 195, 7, 19, 0, 0, 195, 52, 1, 0, 0, 0, 196, 197, 7, 18, 0, 0, 197, 198, 7, 16, 0, 0, 198, 199, 7, 11, 0, 0, 199, 200, 7, 16, 0, 0, 200, 201, 7, 0, 0, 0, 201, 202, 7, 0, 0, 0, 202, 54, 1, 0, 0, 0, 203, 204, 7, 6, 0, 0, 204, 205, 7, 12, 0, 0, 205, 206, 7, 20, 0, 0, 206, 213, 7, 3, 0, 0, 207, 208, 7, 21, 0, 0, 208, 209, 7, 16, 0, 0, 209, 210, 7, 0, 0, 0, 210, 211, 7, 11, 0, 0, 211, 213, 7, 3, 0, 0, 212, 203, 1, 0, 0, 0, 212, 207, 1, 0, 0, 0, 213, 56, 1, 0, 0, 0, 214, 216, 3, 65, 32, 0, 215, 214, 1, 0, 0, 0, 216, 217, 1, 0, 0, 0, 217, 215, 1, 0, 0, 0, 217, 218, 1, 0, 0, 0, 218, 225, 1, 0, 0, 0, 219, 221, 5, 46, 0, 0, 220, 222, 3, 65, 32, 0, 221, 220, 1, 0, 0, 0, 222, 223, 1, 0, 0, 0, 223, 221, 1, 0, 0, 0, 223, 224, 1, 0, 0, 0, 224, 226, 1, 0, 0, 0, 225, 219, 1, 0, 0, 0, 225, 226, 1, 0, 0, 0, 226, 58, 1, 0, 0, 0, 227, 233, 5, 34, 0, 0, 228, 232, 8, 22, 0, 0, 229, 230, 5, 92, 0, 0, 230, 232, 9, 0, 0, 0, 231, 228, 1, 0, 0, 0, 231, 229, 1, 0, 0, 0, 232, 235, 1, 0, 0, 0, 233, 231, 1, 0, 0, 0, 233, 234, 1, 0, 0, 0, 234, 236, 1, 0, 0, 0, 235, 233, 1, 0, 0, 0, 236, 248, 5, 34, 0, 0, 237, 243, 5, 39, 0, 0, 238, 242, 8, 23, 0, 0, 239, 240, 5, 92, 0, 0, 240, 242, 9, 0, 0, 0, 241, 238, 1, 0, 0, 0, 241, 239, 1, 0, 0, 0, 242, 245, 1, 0, 0, 0, 243, 241, 1, 0, 0, 0, 243, 244, 1, 0, 0, 0, 244, 246, 1, 0, 0, 0, 245, 243, 1, 0, 0, 0, 246, 248, 5, 39, 0, 0, 247, 227, 1, 0, 0, 0, 247, 237, 1, 0, 0, 0, 248, 60, 1, 0, 0, 0, 249, 253, 7, 24, 0, 0, 250, 252, 7, 25, 0, 0, 251, 250, 1, 0, 0, 0, 252, 255, 1, 0, 0, 0, 253, 251, 1, 0, 0, 0, 253, 254, 1, 0, 0, 0, 254, 62, 1, 0, 0, 0, 255, 253, 1, 0, 0, 0, 256, 258, 7, 26, 0, 0, 257, 256, 1, 0, 0, 0, 258, 259, 1, 0, 0, 0, 259, 257, 1, 0, 0, 0, 259, 260, 1, 0, 0, 0, 260, 261, 1, 0, 0, 0, 261, 262, 6, 31, 0, 0, 262, 64, 1, 0, 0, 0, 263, 264, 7, 27, 0, 0, 264, 66, 1, 0, 0, 0, 265, 267, 8, 28, 0, 0, 266, 265, 1, 0, 0, 0, 267, 268, 1, 0, 0, 0, 268, 266, 1, 0, 0, 0, 268, 269, 1, 0, 0, 0, 269, 68, 1, 0, 0, 0, 18, 0, 82, 111, 130, 152, 169, 212, 217, 223, 225, 231, 233, 241, 243, 247, 253, 259, 268, 1, 6, 0, 0] \ No newline at end of file +[4, 0, 33, 334, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 3, 5, 91, 8, 5, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 4, 13, 118, 8, 13, 11, 13, 12, 13, 119, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 4, 15, 137, 8, 15, 11, 15, 12, 15, 138, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 3, 17, 161, 8, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 3, 19, 178, 8, 19, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 3, 27, 221, 8, 27, 1, 28, 1, 28, 1, 29, 3, 29, 226, 8, 29, 1, 29, 4, 29, 229, 8, 29, 11, 29, 12, 29, 230, 1, 29, 1, 29, 5, 29, 235, 8, 29, 10, 29, 12, 29, 238, 9, 29, 3, 29, 240, 8, 29, 1, 29, 1, 29, 3, 29, 244, 8, 29, 1, 29, 4, 29, 247, 8, 29, 11, 29, 12, 29, 248, 3, 29, 251, 8, 29, 1, 29, 3, 29, 254, 8, 29, 1, 29, 1, 29, 4, 29, 258, 8, 29, 11, 29, 12, 29, 259, 1, 29, 1, 29, 3, 29, 264, 8, 29, 1, 29, 4, 29, 267, 8, 29, 11, 29, 12, 29, 268, 3, 29, 271, 8, 29, 3, 29, 273, 8, 29, 1, 30, 1, 30, 1, 30, 1, 30, 5, 30, 279, 8, 30, 10, 30, 12, 30, 282, 9, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 5, 30, 289, 8, 30, 10, 30, 12, 30, 292, 9, 30, 1, 30, 3, 30, 295, 8, 30, 1, 31, 1, 31, 5, 31, 299, 8, 31, 10, 31, 12, 31, 302, 9, 31, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 316, 8, 34, 10, 34, 12, 34, 319, 9, 34, 1, 35, 4, 35, 322, 8, 35, 11, 35, 12, 35, 323, 1, 35, 1, 35, 1, 36, 1, 36, 1, 37, 4, 37, 331, 8, 37, 11, 37, 12, 37, 332, 0, 0, 38, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 0, 59, 29, 61, 30, 63, 0, 65, 0, 67, 0, 69, 31, 71, 32, 73, 0, 75, 33, 1, 0, 30, 2, 0, 76, 76, 108, 108, 2, 0, 73, 73, 105, 105, 2, 0, 75, 75, 107, 107, 2, 0, 69, 69, 101, 101, 2, 0, 78, 78, 110, 110, 2, 0, 79, 79, 111, 111, 2, 0, 84, 84, 116, 116, 2, 0, 9, 9, 32, 32, 2, 0, 66, 66, 98, 98, 2, 0, 87, 87, 119, 119, 2, 0, 88, 88, 120, 120, 2, 0, 83, 83, 115, 115, 2, 0, 82, 82, 114, 114, 2, 0, 71, 71, 103, 103, 2, 0, 80, 80, 112, 112, 2, 0, 67, 67, 99, 99, 2, 0, 65, 65, 97, 97, 2, 0, 68, 68, 100, 100, 2, 0, 72, 72, 104, 104, 2, 0, 89, 89, 121, 121, 2, 0, 85, 85, 117, 117, 2, 0, 70, 70, 102, 102, 2, 0, 43, 43, 45, 45, 2, 0, 34, 34, 92, 92, 2, 0, 39, 39, 92, 92, 2, 0, 65, 90, 97, 122, 5, 0, 45, 45, 48, 58, 65, 90, 95, 95, 97, 122, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, 8, 0, 9, 10, 13, 13, 32, 34, 39, 41, 44, 44, 60, 62, 91, 91, 93, 93, 358, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 1, 77, 1, 0, 0, 0, 3, 79, 1, 0, 0, 0, 5, 81, 1, 0, 0, 0, 7, 83, 1, 0, 0, 0, 9, 85, 1, 0, 0, 0, 11, 90, 1, 0, 0, 0, 13, 92, 1, 0, 0, 0, 15, 95, 1, 0, 0, 0, 17, 98, 1, 0, 0, 0, 19, 100, 1, 0, 0, 0, 21, 103, 1, 0, 0, 0, 23, 105, 1, 0, 0, 0, 25, 108, 1, 0, 0, 0, 27, 113, 1, 0, 0, 0, 29, 126, 1, 0, 0, 0, 31, 132, 1, 0, 0, 0, 33, 146, 1, 0, 0, 0, 35, 154, 1, 0, 0, 0, 37, 162, 1, 0, 0, 0, 39, 169, 1, 0, 0, 0, 41, 179, 1, 0, 0, 0, 43, 182, 1, 0, 0, 0, 45, 186, 1, 0, 0, 0, 47, 190, 1, 0, 0, 0, 49, 193, 1, 0, 0, 0, 51, 197, 1, 0, 0, 0, 53, 204, 1, 0, 0, 0, 55, 220, 1, 0, 0, 0, 57, 222, 1, 0, 0, 0, 59, 272, 1, 0, 0, 0, 61, 294, 1, 0, 0, 0, 63, 296, 1, 0, 0, 0, 65, 303, 1, 0, 0, 0, 67, 306, 1, 0, 0, 0, 69, 310, 1, 0, 0, 0, 71, 321, 1, 0, 0, 0, 73, 327, 1, 0, 0, 0, 75, 330, 1, 0, 0, 0, 77, 78, 5, 40, 0, 0, 78, 2, 1, 0, 0, 0, 79, 80, 5, 41, 0, 0, 80, 4, 1, 0, 0, 0, 81, 82, 5, 91, 0, 0, 82, 6, 1, 0, 0, 0, 83, 84, 5, 93, 0, 0, 84, 8, 1, 0, 0, 0, 85, 86, 5, 44, 0, 0, 86, 10, 1, 0, 0, 0, 87, 91, 5, 61, 0, 0, 88, 89, 5, 61, 0, 0, 89, 91, 5, 61, 0, 0, 90, 87, 1, 0, 0, 0, 90, 88, 1, 0, 0, 0, 91, 12, 1, 0, 0, 0, 92, 93, 5, 33, 0, 0, 93, 94, 5, 61, 0, 0, 94, 14, 1, 0, 0, 0, 95, 96, 5, 60, 0, 0, 96, 97, 5, 62, 0, 0, 97, 16, 1, 0, 0, 0, 98, 99, 5, 60, 0, 0, 99, 18, 1, 0, 0, 0, 100, 101, 5, 60, 0, 0, 101, 102, 5, 61, 0, 0, 102, 20, 1, 0, 0, 0, 103, 104, 5, 62, 0, 0, 104, 22, 1, 0, 0, 0, 105, 106, 5, 62, 0, 0, 106, 107, 5, 61, 0, 0, 107, 24, 1, 0, 0, 0, 108, 109, 7, 0, 0, 0, 109, 110, 7, 1, 0, 0, 110, 111, 7, 2, 0, 0, 111, 112, 7, 3, 0, 0, 112, 26, 1, 0, 0, 0, 113, 114, 7, 4, 0, 0, 114, 115, 7, 5, 0, 0, 115, 117, 7, 6, 0, 0, 116, 118, 7, 7, 0, 0, 117, 116, 1, 0, 0, 0, 118, 119, 1, 0, 0, 0, 119, 117, 1, 0, 0, 0, 119, 120, 1, 0, 0, 0, 120, 121, 1, 0, 0, 0, 121, 122, 7, 0, 0, 0, 122, 123, 7, 1, 0, 0, 123, 124, 7, 2, 0, 0, 124, 125, 7, 3, 0, 0, 125, 28, 1, 0, 0, 0, 126, 127, 7, 1, 0, 0, 127, 128, 7, 0, 0, 0, 128, 129, 7, 1, 0, 0, 129, 130, 7, 2, 0, 0, 130, 131, 7, 3, 0, 0, 131, 30, 1, 0, 0, 0, 132, 133, 7, 4, 0, 0, 133, 134, 7, 5, 0, 0, 134, 136, 7, 6, 0, 0, 135, 137, 7, 7, 0, 0, 136, 135, 1, 0, 0, 0, 137, 138, 1, 0, 0, 0, 138, 136, 1, 0, 0, 0, 138, 139, 1, 0, 0, 0, 139, 140, 1, 0, 0, 0, 140, 141, 7, 1, 0, 0, 141, 142, 7, 0, 0, 0, 142, 143, 7, 1, 0, 0, 143, 144, 7, 2, 0, 0, 144, 145, 7, 3, 0, 0, 145, 32, 1, 0, 0, 0, 146, 147, 7, 8, 0, 0, 147, 148, 7, 3, 0, 0, 148, 149, 7, 6, 0, 0, 149, 150, 7, 9, 0, 0, 150, 151, 7, 3, 0, 0, 151, 152, 7, 3, 0, 0, 152, 153, 7, 4, 0, 0, 153, 34, 1, 0, 0, 0, 154, 155, 7, 3, 0, 0, 155, 156, 7, 10, 0, 0, 156, 157, 7, 1, 0, 0, 157, 158, 7, 11, 0, 0, 158, 160, 7, 6, 0, 0, 159, 161, 7, 11, 0, 0, 160, 159, 1, 0, 0, 0, 160, 161, 1, 0, 0, 0, 161, 36, 1, 0, 0, 0, 162, 163, 7, 12, 0, 0, 163, 164, 7, 3, 0, 0, 164, 165, 7, 13, 0, 0, 165, 166, 7, 3, 0, 0, 166, 167, 7, 10, 0, 0, 167, 168, 7, 14, 0, 0, 168, 38, 1, 0, 0, 0, 169, 170, 7, 15, 0, 0, 170, 171, 7, 5, 0, 0, 171, 172, 7, 4, 0, 0, 172, 173, 7, 6, 0, 0, 173, 174, 7, 16, 0, 0, 174, 175, 7, 1, 0, 0, 175, 177, 7, 4, 0, 0, 176, 178, 7, 11, 0, 0, 177, 176, 1, 0, 0, 0, 177, 178, 1, 0, 0, 0, 178, 40, 1, 0, 0, 0, 179, 180, 7, 1, 0, 0, 180, 181, 7, 4, 0, 0, 181, 42, 1, 0, 0, 0, 182, 183, 7, 4, 0, 0, 183, 184, 7, 5, 0, 0, 184, 185, 7, 6, 0, 0, 185, 44, 1, 0, 0, 0, 186, 187, 7, 16, 0, 0, 187, 188, 7, 4, 0, 0, 188, 189, 7, 17, 0, 0, 189, 46, 1, 0, 0, 0, 190, 191, 7, 5, 0, 0, 191, 192, 7, 12, 0, 0, 192, 48, 1, 0, 0, 0, 193, 194, 7, 18, 0, 0, 194, 195, 7, 16, 0, 0, 195, 196, 7, 11, 0, 0, 196, 50, 1, 0, 0, 0, 197, 198, 7, 18, 0, 0, 198, 199, 7, 16, 0, 0, 199, 200, 7, 11, 0, 0, 200, 201, 7, 16, 0, 0, 201, 202, 7, 4, 0, 0, 202, 203, 7, 19, 0, 0, 203, 52, 1, 0, 0, 0, 204, 205, 7, 18, 0, 0, 205, 206, 7, 16, 0, 0, 206, 207, 7, 11, 0, 0, 207, 208, 7, 16, 0, 0, 208, 209, 7, 0, 0, 0, 209, 210, 7, 0, 0, 0, 210, 54, 1, 0, 0, 0, 211, 212, 7, 6, 0, 0, 212, 213, 7, 12, 0, 0, 213, 214, 7, 20, 0, 0, 214, 221, 7, 3, 0, 0, 215, 216, 7, 21, 0, 0, 216, 217, 7, 16, 0, 0, 217, 218, 7, 0, 0, 0, 218, 219, 7, 11, 0, 0, 219, 221, 7, 3, 0, 0, 220, 211, 1, 0, 0, 0, 220, 215, 1, 0, 0, 0, 221, 56, 1, 0, 0, 0, 222, 223, 7, 22, 0, 0, 223, 58, 1, 0, 0, 0, 224, 226, 3, 57, 28, 0, 225, 224, 1, 0, 0, 0, 225, 226, 1, 0, 0, 0, 226, 228, 1, 0, 0, 0, 227, 229, 3, 73, 36, 0, 228, 227, 1, 0, 0, 0, 229, 230, 1, 0, 0, 0, 230, 228, 1, 0, 0, 0, 230, 231, 1, 0, 0, 0, 231, 239, 1, 0, 0, 0, 232, 236, 5, 46, 0, 0, 233, 235, 3, 73, 36, 0, 234, 233, 1, 0, 0, 0, 235, 238, 1, 0, 0, 0, 236, 234, 1, 0, 0, 0, 236, 237, 1, 0, 0, 0, 237, 240, 1, 0, 0, 0, 238, 236, 1, 0, 0, 0, 239, 232, 1, 0, 0, 0, 239, 240, 1, 0, 0, 0, 240, 250, 1, 0, 0, 0, 241, 243, 7, 3, 0, 0, 242, 244, 3, 57, 28, 0, 243, 242, 1, 0, 0, 0, 243, 244, 1, 0, 0, 0, 244, 246, 1, 0, 0, 0, 245, 247, 3, 73, 36, 0, 246, 245, 1, 0, 0, 0, 247, 248, 1, 0, 0, 0, 248, 246, 1, 0, 0, 0, 248, 249, 1, 0, 0, 0, 249, 251, 1, 0, 0, 0, 250, 241, 1, 0, 0, 0, 250, 251, 1, 0, 0, 0, 251, 273, 1, 0, 0, 0, 252, 254, 3, 57, 28, 0, 253, 252, 1, 0, 0, 0, 253, 254, 1, 0, 0, 0, 254, 255, 1, 0, 0, 0, 255, 257, 5, 46, 0, 0, 256, 258, 3, 73, 36, 0, 257, 256, 1, 0, 0, 0, 258, 259, 1, 0, 0, 0, 259, 257, 1, 0, 0, 0, 259, 260, 1, 0, 0, 0, 260, 270, 1, 0, 0, 0, 261, 263, 7, 3, 0, 0, 262, 264, 3, 57, 28, 0, 263, 262, 1, 0, 0, 0, 263, 264, 1, 0, 0, 0, 264, 266, 1, 0, 0, 0, 265, 267, 3, 73, 36, 0, 266, 265, 1, 0, 0, 0, 267, 268, 1, 0, 0, 0, 268, 266, 1, 0, 0, 0, 268, 269, 1, 0, 0, 0, 269, 271, 1, 0, 0, 0, 270, 261, 1, 0, 0, 0, 270, 271, 1, 0, 0, 0, 271, 273, 1, 0, 0, 0, 272, 225, 1, 0, 0, 0, 272, 253, 1, 0, 0, 0, 273, 60, 1, 0, 0, 0, 274, 280, 5, 34, 0, 0, 275, 279, 8, 23, 0, 0, 276, 277, 5, 92, 0, 0, 277, 279, 9, 0, 0, 0, 278, 275, 1, 0, 0, 0, 278, 276, 1, 0, 0, 0, 279, 282, 1, 0, 0, 0, 280, 278, 1, 0, 0, 0, 280, 281, 1, 0, 0, 0, 281, 283, 1, 0, 0, 0, 282, 280, 1, 0, 0, 0, 283, 295, 5, 34, 0, 0, 284, 290, 5, 39, 0, 0, 285, 289, 8, 24, 0, 0, 286, 287, 5, 92, 0, 0, 287, 289, 9, 0, 0, 0, 288, 285, 1, 0, 0, 0, 288, 286, 1, 0, 0, 0, 289, 292, 1, 0, 0, 0, 290, 288, 1, 0, 0, 0, 290, 291, 1, 0, 0, 0, 291, 293, 1, 0, 0, 0, 292, 290, 1, 0, 0, 0, 293, 295, 5, 39, 0, 0, 294, 274, 1, 0, 0, 0, 294, 284, 1, 0, 0, 0, 295, 62, 1, 0, 0, 0, 296, 300, 7, 25, 0, 0, 297, 299, 7, 26, 0, 0, 298, 297, 1, 0, 0, 0, 299, 302, 1, 0, 0, 0, 300, 298, 1, 0, 0, 0, 300, 301, 1, 0, 0, 0, 301, 64, 1, 0, 0, 0, 302, 300, 1, 0, 0, 0, 303, 304, 5, 91, 0, 0, 304, 305, 5, 93, 0, 0, 305, 66, 1, 0, 0, 0, 306, 307, 5, 91, 0, 0, 307, 308, 5, 42, 0, 0, 308, 309, 5, 93, 0, 0, 309, 68, 1, 0, 0, 0, 310, 317, 3, 63, 31, 0, 311, 312, 5, 46, 0, 0, 312, 316, 3, 63, 31, 0, 313, 316, 3, 65, 32, 0, 314, 316, 3, 67, 33, 0, 315, 311, 1, 0, 0, 0, 315, 313, 1, 0, 0, 0, 315, 314, 1, 0, 0, 0, 316, 319, 1, 0, 0, 0, 317, 315, 1, 0, 0, 0, 317, 318, 1, 0, 0, 0, 318, 70, 1, 0, 0, 0, 319, 317, 1, 0, 0, 0, 320, 322, 7, 27, 0, 0, 321, 320, 1, 0, 0, 0, 322, 323, 1, 0, 0, 0, 323, 321, 1, 0, 0, 0, 323, 324, 1, 0, 0, 0, 324, 325, 1, 0, 0, 0, 325, 326, 6, 35, 0, 0, 326, 72, 1, 0, 0, 0, 327, 328, 7, 28, 0, 0, 328, 74, 1, 0, 0, 0, 329, 331, 8, 29, 0, 0, 330, 329, 1, 0, 0, 0, 331, 332, 1, 0, 0, 0, 332, 330, 1, 0, 0, 0, 332, 333, 1, 0, 0, 0, 333, 76, 1, 0, 0, 0, 30, 0, 90, 119, 138, 160, 177, 220, 225, 230, 236, 239, 243, 248, 250, 253, 259, 263, 268, 270, 272, 278, 280, 288, 290, 294, 300, 315, 317, 323, 332, 1, 6, 0, 0] \ No newline at end of file diff --git a/pkg/parser/grammar/filterquery_lexer.go b/pkg/parser/grammar/filterquery_lexer.go index e1588f5adb..2903775376 100644 --- a/pkg/parser/grammar/filterquery_lexer.go +++ b/pkg/parser/grammar/filterquery_lexer.go @@ -57,139 +57,171 @@ func filterquerylexerLexerInit() { "LPAREN", "RPAREN", "LBRACK", "RBRACK", "COMMA", "EQUALS", "NOT_EQUALS", "NEQ", "LT", "LE", "GT", "GE", "LIKE", "NOT_LIKE", "ILIKE", "NOT_ILIKE", "BETWEEN", "EXISTS", "REGEXP", "CONTAINS", "IN", "NOT", "AND", "OR", - "HAS", "HASANY", "HASALL", "BOOL", "NUMBER", "QUOTED_TEXT", "KEY", "WS", - "DIGIT", "FREETEXT", + "HAS", "HASANY", "HASALL", "BOOL", "SIGN", "NUMBER", "QUOTED_TEXT", + "SEGMENT", "EMPTY_BRACKS", "OLD_JSON_BRACKS", "KEY", "WS", "DIGIT", + "FREETEXT", } staticData.PredictionContextCache = antlr.NewPredictionContextCache() staticData.serializedATN = []int32{ - 4, 0, 33, 270, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, + 4, 0, 33, 334, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, - 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, - 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 3, 5, 83, 8, 5, 1, 6, 1, 6, - 1, 6, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, - 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, - 1, 13, 4, 13, 110, 8, 13, 11, 13, 12, 13, 111, 1, 13, 1, 13, 1, 13, 1, - 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, - 1, 15, 4, 15, 129, 8, 15, 11, 15, 12, 15, 130, 1, 15, 1, 15, 1, 15, 1, - 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, - 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 3, 17, 153, 8, 17, 1, 18, 1, - 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, - 1, 19, 1, 19, 1, 19, 3, 19, 170, 8, 19, 1, 20, 1, 20, 1, 20, 1, 21, 1, - 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 24, - 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, - 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, - 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 3, 27, 213, 8, 27, 1, 28, 4, 28, 216, - 8, 28, 11, 28, 12, 28, 217, 1, 28, 1, 28, 4, 28, 222, 8, 28, 11, 28, 12, - 28, 223, 3, 28, 226, 8, 28, 1, 29, 1, 29, 1, 29, 1, 29, 5, 29, 232, 8, - 29, 10, 29, 12, 29, 235, 9, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 5, 29, - 242, 8, 29, 10, 29, 12, 29, 245, 9, 29, 1, 29, 3, 29, 248, 8, 29, 1, 30, - 1, 30, 5, 30, 252, 8, 30, 10, 30, 12, 30, 255, 9, 30, 1, 31, 4, 31, 258, - 8, 31, 11, 31, 12, 31, 259, 1, 31, 1, 31, 1, 32, 1, 32, 1, 33, 4, 33, 267, - 8, 33, 11, 33, 12, 33, 268, 0, 0, 34, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, - 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, - 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, - 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 0, - 67, 33, 1, 0, 29, 2, 0, 76, 76, 108, 108, 2, 0, 73, 73, 105, 105, 2, 0, - 75, 75, 107, 107, 2, 0, 69, 69, 101, 101, 2, 0, 78, 78, 110, 110, 2, 0, - 79, 79, 111, 111, 2, 0, 84, 84, 116, 116, 2, 0, 9, 9, 32, 32, 2, 0, 66, - 66, 98, 98, 2, 0, 87, 87, 119, 119, 2, 0, 88, 88, 120, 120, 2, 0, 83, 83, - 115, 115, 2, 0, 82, 82, 114, 114, 2, 0, 71, 71, 103, 103, 2, 0, 80, 80, - 112, 112, 2, 0, 67, 67, 99, 99, 2, 0, 65, 65, 97, 97, 2, 0, 68, 68, 100, - 100, 2, 0, 72, 72, 104, 104, 2, 0, 89, 89, 121, 121, 2, 0, 85, 85, 117, - 117, 2, 0, 70, 70, 102, 102, 2, 0, 34, 34, 92, 92, 2, 0, 39, 39, 92, 92, - 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 7, 0, 42, 42, 46, 46, 48, 57, 65, - 91, 93, 93, 95, 95, 97, 122, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, - 8, 0, 9, 10, 13, 13, 32, 34, 39, 41, 44, 44, 60, 62, 91, 91, 93, 93, 285, - 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, - 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, - 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, - 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, - 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, - 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, - 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, - 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, - 0, 0, 63, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 1, 69, 1, 0, 0, 0, 3, 71, 1, 0, - 0, 0, 5, 73, 1, 0, 0, 0, 7, 75, 1, 0, 0, 0, 9, 77, 1, 0, 0, 0, 11, 82, - 1, 0, 0, 0, 13, 84, 1, 0, 0, 0, 15, 87, 1, 0, 0, 0, 17, 90, 1, 0, 0, 0, - 19, 92, 1, 0, 0, 0, 21, 95, 1, 0, 0, 0, 23, 97, 1, 0, 0, 0, 25, 100, 1, - 0, 0, 0, 27, 105, 1, 0, 0, 0, 29, 118, 1, 0, 0, 0, 31, 124, 1, 0, 0, 0, - 33, 138, 1, 0, 0, 0, 35, 146, 1, 0, 0, 0, 37, 154, 1, 0, 0, 0, 39, 161, - 1, 0, 0, 0, 41, 171, 1, 0, 0, 0, 43, 174, 1, 0, 0, 0, 45, 178, 1, 0, 0, - 0, 47, 182, 1, 0, 0, 0, 49, 185, 1, 0, 0, 0, 51, 189, 1, 0, 0, 0, 53, 196, - 1, 0, 0, 0, 55, 212, 1, 0, 0, 0, 57, 215, 1, 0, 0, 0, 59, 247, 1, 0, 0, - 0, 61, 249, 1, 0, 0, 0, 63, 257, 1, 0, 0, 0, 65, 263, 1, 0, 0, 0, 67, 266, - 1, 0, 0, 0, 69, 70, 5, 40, 0, 0, 70, 2, 1, 0, 0, 0, 71, 72, 5, 41, 0, 0, - 72, 4, 1, 0, 0, 0, 73, 74, 5, 91, 0, 0, 74, 6, 1, 0, 0, 0, 75, 76, 5, 93, - 0, 0, 76, 8, 1, 0, 0, 0, 77, 78, 5, 44, 0, 0, 78, 10, 1, 0, 0, 0, 79, 83, - 5, 61, 0, 0, 80, 81, 5, 61, 0, 0, 81, 83, 5, 61, 0, 0, 82, 79, 1, 0, 0, - 0, 82, 80, 1, 0, 0, 0, 83, 12, 1, 0, 0, 0, 84, 85, 5, 33, 0, 0, 85, 86, - 5, 61, 0, 0, 86, 14, 1, 0, 0, 0, 87, 88, 5, 60, 0, 0, 88, 89, 5, 62, 0, - 0, 89, 16, 1, 0, 0, 0, 90, 91, 5, 60, 0, 0, 91, 18, 1, 0, 0, 0, 92, 93, - 5, 60, 0, 0, 93, 94, 5, 61, 0, 0, 94, 20, 1, 0, 0, 0, 95, 96, 5, 62, 0, - 0, 96, 22, 1, 0, 0, 0, 97, 98, 5, 62, 0, 0, 98, 99, 5, 61, 0, 0, 99, 24, - 1, 0, 0, 0, 100, 101, 7, 0, 0, 0, 101, 102, 7, 1, 0, 0, 102, 103, 7, 2, - 0, 0, 103, 104, 7, 3, 0, 0, 104, 26, 1, 0, 0, 0, 105, 106, 7, 4, 0, 0, - 106, 107, 7, 5, 0, 0, 107, 109, 7, 6, 0, 0, 108, 110, 7, 7, 0, 0, 109, - 108, 1, 0, 0, 0, 110, 111, 1, 0, 0, 0, 111, 109, 1, 0, 0, 0, 111, 112, - 1, 0, 0, 0, 112, 113, 1, 0, 0, 0, 113, 114, 7, 0, 0, 0, 114, 115, 7, 1, - 0, 0, 115, 116, 7, 2, 0, 0, 116, 117, 7, 3, 0, 0, 117, 28, 1, 0, 0, 0, - 118, 119, 7, 1, 0, 0, 119, 120, 7, 0, 0, 0, 120, 121, 7, 1, 0, 0, 121, - 122, 7, 2, 0, 0, 122, 123, 7, 3, 0, 0, 123, 30, 1, 0, 0, 0, 124, 125, 7, - 4, 0, 0, 125, 126, 7, 5, 0, 0, 126, 128, 7, 6, 0, 0, 127, 129, 7, 7, 0, - 0, 128, 127, 1, 0, 0, 0, 129, 130, 1, 0, 0, 0, 130, 128, 1, 0, 0, 0, 130, - 131, 1, 0, 0, 0, 131, 132, 1, 0, 0, 0, 132, 133, 7, 1, 0, 0, 133, 134, - 7, 0, 0, 0, 134, 135, 7, 1, 0, 0, 135, 136, 7, 2, 0, 0, 136, 137, 7, 3, - 0, 0, 137, 32, 1, 0, 0, 0, 138, 139, 7, 8, 0, 0, 139, 140, 7, 3, 0, 0, - 140, 141, 7, 6, 0, 0, 141, 142, 7, 9, 0, 0, 142, 143, 7, 3, 0, 0, 143, - 144, 7, 3, 0, 0, 144, 145, 7, 4, 0, 0, 145, 34, 1, 0, 0, 0, 146, 147, 7, - 3, 0, 0, 147, 148, 7, 10, 0, 0, 148, 149, 7, 1, 0, 0, 149, 150, 7, 11, - 0, 0, 150, 152, 7, 6, 0, 0, 151, 153, 7, 11, 0, 0, 152, 151, 1, 0, 0, 0, - 152, 153, 1, 0, 0, 0, 153, 36, 1, 0, 0, 0, 154, 155, 7, 12, 0, 0, 155, - 156, 7, 3, 0, 0, 156, 157, 7, 13, 0, 0, 157, 158, 7, 3, 0, 0, 158, 159, - 7, 10, 0, 0, 159, 160, 7, 14, 0, 0, 160, 38, 1, 0, 0, 0, 161, 162, 7, 15, - 0, 0, 162, 163, 7, 5, 0, 0, 163, 164, 7, 4, 0, 0, 164, 165, 7, 6, 0, 0, - 165, 166, 7, 16, 0, 0, 166, 167, 7, 1, 0, 0, 167, 169, 7, 4, 0, 0, 168, - 170, 7, 11, 0, 0, 169, 168, 1, 0, 0, 0, 169, 170, 1, 0, 0, 0, 170, 40, - 1, 0, 0, 0, 171, 172, 7, 1, 0, 0, 172, 173, 7, 4, 0, 0, 173, 42, 1, 0, - 0, 0, 174, 175, 7, 4, 0, 0, 175, 176, 7, 5, 0, 0, 176, 177, 7, 6, 0, 0, - 177, 44, 1, 0, 0, 0, 178, 179, 7, 16, 0, 0, 179, 180, 7, 4, 0, 0, 180, - 181, 7, 17, 0, 0, 181, 46, 1, 0, 0, 0, 182, 183, 7, 5, 0, 0, 183, 184, - 7, 12, 0, 0, 184, 48, 1, 0, 0, 0, 185, 186, 7, 18, 0, 0, 186, 187, 7, 16, - 0, 0, 187, 188, 7, 11, 0, 0, 188, 50, 1, 0, 0, 0, 189, 190, 7, 18, 0, 0, - 190, 191, 7, 16, 0, 0, 191, 192, 7, 11, 0, 0, 192, 193, 7, 16, 0, 0, 193, - 194, 7, 4, 0, 0, 194, 195, 7, 19, 0, 0, 195, 52, 1, 0, 0, 0, 196, 197, - 7, 18, 0, 0, 197, 198, 7, 16, 0, 0, 198, 199, 7, 11, 0, 0, 199, 200, 7, - 16, 0, 0, 200, 201, 7, 0, 0, 0, 201, 202, 7, 0, 0, 0, 202, 54, 1, 0, 0, - 0, 203, 204, 7, 6, 0, 0, 204, 205, 7, 12, 0, 0, 205, 206, 7, 20, 0, 0, - 206, 213, 7, 3, 0, 0, 207, 208, 7, 21, 0, 0, 208, 209, 7, 16, 0, 0, 209, - 210, 7, 0, 0, 0, 210, 211, 7, 11, 0, 0, 211, 213, 7, 3, 0, 0, 212, 203, - 1, 0, 0, 0, 212, 207, 1, 0, 0, 0, 213, 56, 1, 0, 0, 0, 214, 216, 3, 65, - 32, 0, 215, 214, 1, 0, 0, 0, 216, 217, 1, 0, 0, 0, 217, 215, 1, 0, 0, 0, - 217, 218, 1, 0, 0, 0, 218, 225, 1, 0, 0, 0, 219, 221, 5, 46, 0, 0, 220, - 222, 3, 65, 32, 0, 221, 220, 1, 0, 0, 0, 222, 223, 1, 0, 0, 0, 223, 221, - 1, 0, 0, 0, 223, 224, 1, 0, 0, 0, 224, 226, 1, 0, 0, 0, 225, 219, 1, 0, - 0, 0, 225, 226, 1, 0, 0, 0, 226, 58, 1, 0, 0, 0, 227, 233, 5, 34, 0, 0, - 228, 232, 8, 22, 0, 0, 229, 230, 5, 92, 0, 0, 230, 232, 9, 0, 0, 0, 231, - 228, 1, 0, 0, 0, 231, 229, 1, 0, 0, 0, 232, 235, 1, 0, 0, 0, 233, 231, - 1, 0, 0, 0, 233, 234, 1, 0, 0, 0, 234, 236, 1, 0, 0, 0, 235, 233, 1, 0, - 0, 0, 236, 248, 5, 34, 0, 0, 237, 243, 5, 39, 0, 0, 238, 242, 8, 23, 0, - 0, 239, 240, 5, 92, 0, 0, 240, 242, 9, 0, 0, 0, 241, 238, 1, 0, 0, 0, 241, - 239, 1, 0, 0, 0, 242, 245, 1, 0, 0, 0, 243, 241, 1, 0, 0, 0, 243, 244, - 1, 0, 0, 0, 244, 246, 1, 0, 0, 0, 245, 243, 1, 0, 0, 0, 246, 248, 5, 39, - 0, 0, 247, 227, 1, 0, 0, 0, 247, 237, 1, 0, 0, 0, 248, 60, 1, 0, 0, 0, - 249, 253, 7, 24, 0, 0, 250, 252, 7, 25, 0, 0, 251, 250, 1, 0, 0, 0, 252, - 255, 1, 0, 0, 0, 253, 251, 1, 0, 0, 0, 253, 254, 1, 0, 0, 0, 254, 62, 1, - 0, 0, 0, 255, 253, 1, 0, 0, 0, 256, 258, 7, 26, 0, 0, 257, 256, 1, 0, 0, - 0, 258, 259, 1, 0, 0, 0, 259, 257, 1, 0, 0, 0, 259, 260, 1, 0, 0, 0, 260, - 261, 1, 0, 0, 0, 261, 262, 6, 31, 0, 0, 262, 64, 1, 0, 0, 0, 263, 264, - 7, 27, 0, 0, 264, 66, 1, 0, 0, 0, 265, 267, 8, 28, 0, 0, 266, 265, 1, 0, - 0, 0, 267, 268, 1, 0, 0, 0, 268, 266, 1, 0, 0, 0, 268, 269, 1, 0, 0, 0, - 269, 68, 1, 0, 0, 0, 18, 0, 82, 111, 130, 152, 169, 212, 217, 223, 225, - 231, 233, 241, 243, 247, 253, 259, 268, 1, 6, 0, 0, + 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, + 7, 36, 2, 37, 7, 37, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, + 4, 1, 4, 1, 5, 1, 5, 1, 5, 3, 5, 91, 8, 5, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, + 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, + 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 4, 13, 118, + 8, 13, 11, 13, 12, 13, 119, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, + 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 4, 15, 137, + 8, 15, 11, 15, 12, 15, 138, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, + 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, + 1, 17, 1, 17, 1, 17, 3, 17, 161, 8, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, + 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, + 3, 19, 178, 8, 19, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, + 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, + 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, + 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, + 1, 27, 1, 27, 3, 27, 221, 8, 27, 1, 28, 1, 28, 1, 29, 3, 29, 226, 8, 29, + 1, 29, 4, 29, 229, 8, 29, 11, 29, 12, 29, 230, 1, 29, 1, 29, 5, 29, 235, + 8, 29, 10, 29, 12, 29, 238, 9, 29, 3, 29, 240, 8, 29, 1, 29, 1, 29, 3, + 29, 244, 8, 29, 1, 29, 4, 29, 247, 8, 29, 11, 29, 12, 29, 248, 3, 29, 251, + 8, 29, 1, 29, 3, 29, 254, 8, 29, 1, 29, 1, 29, 4, 29, 258, 8, 29, 11, 29, + 12, 29, 259, 1, 29, 1, 29, 3, 29, 264, 8, 29, 1, 29, 4, 29, 267, 8, 29, + 11, 29, 12, 29, 268, 3, 29, 271, 8, 29, 3, 29, 273, 8, 29, 1, 30, 1, 30, + 1, 30, 1, 30, 5, 30, 279, 8, 30, 10, 30, 12, 30, 282, 9, 30, 1, 30, 1, + 30, 1, 30, 1, 30, 1, 30, 5, 30, 289, 8, 30, 10, 30, 12, 30, 292, 9, 30, + 1, 30, 3, 30, 295, 8, 30, 1, 31, 1, 31, 5, 31, 299, 8, 31, 10, 31, 12, + 31, 302, 9, 31, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 34, + 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 316, 8, 34, 10, 34, 12, 34, 319, 9, + 34, 1, 35, 4, 35, 322, 8, 35, 11, 35, 12, 35, 323, 1, 35, 1, 35, 1, 36, + 1, 36, 1, 37, 4, 37, 331, 8, 37, 11, 37, 12, 37, 332, 0, 0, 38, 1, 1, 3, + 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, + 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, + 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 0, 59, 29, + 61, 30, 63, 0, 65, 0, 67, 0, 69, 31, 71, 32, 73, 0, 75, 33, 1, 0, 30, 2, + 0, 76, 76, 108, 108, 2, 0, 73, 73, 105, 105, 2, 0, 75, 75, 107, 107, 2, + 0, 69, 69, 101, 101, 2, 0, 78, 78, 110, 110, 2, 0, 79, 79, 111, 111, 2, + 0, 84, 84, 116, 116, 2, 0, 9, 9, 32, 32, 2, 0, 66, 66, 98, 98, 2, 0, 87, + 87, 119, 119, 2, 0, 88, 88, 120, 120, 2, 0, 83, 83, 115, 115, 2, 0, 82, + 82, 114, 114, 2, 0, 71, 71, 103, 103, 2, 0, 80, 80, 112, 112, 2, 0, 67, + 67, 99, 99, 2, 0, 65, 65, 97, 97, 2, 0, 68, 68, 100, 100, 2, 0, 72, 72, + 104, 104, 2, 0, 89, 89, 121, 121, 2, 0, 85, 85, 117, 117, 2, 0, 70, 70, + 102, 102, 2, 0, 43, 43, 45, 45, 2, 0, 34, 34, 92, 92, 2, 0, 39, 39, 92, + 92, 2, 0, 65, 90, 97, 122, 5, 0, 45, 45, 48, 58, 65, 90, 95, 95, 97, 122, + 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, 8, 0, 9, 10, 13, 13, 32, 34, + 39, 41, 44, 44, 60, 62, 91, 91, 93, 93, 358, 0, 1, 1, 0, 0, 0, 0, 3, 1, + 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, + 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, + 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, + 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, + 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, + 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, + 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 59, 1, + 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 75, + 1, 0, 0, 0, 1, 77, 1, 0, 0, 0, 3, 79, 1, 0, 0, 0, 5, 81, 1, 0, 0, 0, 7, + 83, 1, 0, 0, 0, 9, 85, 1, 0, 0, 0, 11, 90, 1, 0, 0, 0, 13, 92, 1, 0, 0, + 0, 15, 95, 1, 0, 0, 0, 17, 98, 1, 0, 0, 0, 19, 100, 1, 0, 0, 0, 21, 103, + 1, 0, 0, 0, 23, 105, 1, 0, 0, 0, 25, 108, 1, 0, 0, 0, 27, 113, 1, 0, 0, + 0, 29, 126, 1, 0, 0, 0, 31, 132, 1, 0, 0, 0, 33, 146, 1, 0, 0, 0, 35, 154, + 1, 0, 0, 0, 37, 162, 1, 0, 0, 0, 39, 169, 1, 0, 0, 0, 41, 179, 1, 0, 0, + 0, 43, 182, 1, 0, 0, 0, 45, 186, 1, 0, 0, 0, 47, 190, 1, 0, 0, 0, 49, 193, + 1, 0, 0, 0, 51, 197, 1, 0, 0, 0, 53, 204, 1, 0, 0, 0, 55, 220, 1, 0, 0, + 0, 57, 222, 1, 0, 0, 0, 59, 272, 1, 0, 0, 0, 61, 294, 1, 0, 0, 0, 63, 296, + 1, 0, 0, 0, 65, 303, 1, 0, 0, 0, 67, 306, 1, 0, 0, 0, 69, 310, 1, 0, 0, + 0, 71, 321, 1, 0, 0, 0, 73, 327, 1, 0, 0, 0, 75, 330, 1, 0, 0, 0, 77, 78, + 5, 40, 0, 0, 78, 2, 1, 0, 0, 0, 79, 80, 5, 41, 0, 0, 80, 4, 1, 0, 0, 0, + 81, 82, 5, 91, 0, 0, 82, 6, 1, 0, 0, 0, 83, 84, 5, 93, 0, 0, 84, 8, 1, + 0, 0, 0, 85, 86, 5, 44, 0, 0, 86, 10, 1, 0, 0, 0, 87, 91, 5, 61, 0, 0, + 88, 89, 5, 61, 0, 0, 89, 91, 5, 61, 0, 0, 90, 87, 1, 0, 0, 0, 90, 88, 1, + 0, 0, 0, 91, 12, 1, 0, 0, 0, 92, 93, 5, 33, 0, 0, 93, 94, 5, 61, 0, 0, + 94, 14, 1, 0, 0, 0, 95, 96, 5, 60, 0, 0, 96, 97, 5, 62, 0, 0, 97, 16, 1, + 0, 0, 0, 98, 99, 5, 60, 0, 0, 99, 18, 1, 0, 0, 0, 100, 101, 5, 60, 0, 0, + 101, 102, 5, 61, 0, 0, 102, 20, 1, 0, 0, 0, 103, 104, 5, 62, 0, 0, 104, + 22, 1, 0, 0, 0, 105, 106, 5, 62, 0, 0, 106, 107, 5, 61, 0, 0, 107, 24, + 1, 0, 0, 0, 108, 109, 7, 0, 0, 0, 109, 110, 7, 1, 0, 0, 110, 111, 7, 2, + 0, 0, 111, 112, 7, 3, 0, 0, 112, 26, 1, 0, 0, 0, 113, 114, 7, 4, 0, 0, + 114, 115, 7, 5, 0, 0, 115, 117, 7, 6, 0, 0, 116, 118, 7, 7, 0, 0, 117, + 116, 1, 0, 0, 0, 118, 119, 1, 0, 0, 0, 119, 117, 1, 0, 0, 0, 119, 120, + 1, 0, 0, 0, 120, 121, 1, 0, 0, 0, 121, 122, 7, 0, 0, 0, 122, 123, 7, 1, + 0, 0, 123, 124, 7, 2, 0, 0, 124, 125, 7, 3, 0, 0, 125, 28, 1, 0, 0, 0, + 126, 127, 7, 1, 0, 0, 127, 128, 7, 0, 0, 0, 128, 129, 7, 1, 0, 0, 129, + 130, 7, 2, 0, 0, 130, 131, 7, 3, 0, 0, 131, 30, 1, 0, 0, 0, 132, 133, 7, + 4, 0, 0, 133, 134, 7, 5, 0, 0, 134, 136, 7, 6, 0, 0, 135, 137, 7, 7, 0, + 0, 136, 135, 1, 0, 0, 0, 137, 138, 1, 0, 0, 0, 138, 136, 1, 0, 0, 0, 138, + 139, 1, 0, 0, 0, 139, 140, 1, 0, 0, 0, 140, 141, 7, 1, 0, 0, 141, 142, + 7, 0, 0, 0, 142, 143, 7, 1, 0, 0, 143, 144, 7, 2, 0, 0, 144, 145, 7, 3, + 0, 0, 145, 32, 1, 0, 0, 0, 146, 147, 7, 8, 0, 0, 147, 148, 7, 3, 0, 0, + 148, 149, 7, 6, 0, 0, 149, 150, 7, 9, 0, 0, 150, 151, 7, 3, 0, 0, 151, + 152, 7, 3, 0, 0, 152, 153, 7, 4, 0, 0, 153, 34, 1, 0, 0, 0, 154, 155, 7, + 3, 0, 0, 155, 156, 7, 10, 0, 0, 156, 157, 7, 1, 0, 0, 157, 158, 7, 11, + 0, 0, 158, 160, 7, 6, 0, 0, 159, 161, 7, 11, 0, 0, 160, 159, 1, 0, 0, 0, + 160, 161, 1, 0, 0, 0, 161, 36, 1, 0, 0, 0, 162, 163, 7, 12, 0, 0, 163, + 164, 7, 3, 0, 0, 164, 165, 7, 13, 0, 0, 165, 166, 7, 3, 0, 0, 166, 167, + 7, 10, 0, 0, 167, 168, 7, 14, 0, 0, 168, 38, 1, 0, 0, 0, 169, 170, 7, 15, + 0, 0, 170, 171, 7, 5, 0, 0, 171, 172, 7, 4, 0, 0, 172, 173, 7, 6, 0, 0, + 173, 174, 7, 16, 0, 0, 174, 175, 7, 1, 0, 0, 175, 177, 7, 4, 0, 0, 176, + 178, 7, 11, 0, 0, 177, 176, 1, 0, 0, 0, 177, 178, 1, 0, 0, 0, 178, 40, + 1, 0, 0, 0, 179, 180, 7, 1, 0, 0, 180, 181, 7, 4, 0, 0, 181, 42, 1, 0, + 0, 0, 182, 183, 7, 4, 0, 0, 183, 184, 7, 5, 0, 0, 184, 185, 7, 6, 0, 0, + 185, 44, 1, 0, 0, 0, 186, 187, 7, 16, 0, 0, 187, 188, 7, 4, 0, 0, 188, + 189, 7, 17, 0, 0, 189, 46, 1, 0, 0, 0, 190, 191, 7, 5, 0, 0, 191, 192, + 7, 12, 0, 0, 192, 48, 1, 0, 0, 0, 193, 194, 7, 18, 0, 0, 194, 195, 7, 16, + 0, 0, 195, 196, 7, 11, 0, 0, 196, 50, 1, 0, 0, 0, 197, 198, 7, 18, 0, 0, + 198, 199, 7, 16, 0, 0, 199, 200, 7, 11, 0, 0, 200, 201, 7, 16, 0, 0, 201, + 202, 7, 4, 0, 0, 202, 203, 7, 19, 0, 0, 203, 52, 1, 0, 0, 0, 204, 205, + 7, 18, 0, 0, 205, 206, 7, 16, 0, 0, 206, 207, 7, 11, 0, 0, 207, 208, 7, + 16, 0, 0, 208, 209, 7, 0, 0, 0, 209, 210, 7, 0, 0, 0, 210, 54, 1, 0, 0, + 0, 211, 212, 7, 6, 0, 0, 212, 213, 7, 12, 0, 0, 213, 214, 7, 20, 0, 0, + 214, 221, 7, 3, 0, 0, 215, 216, 7, 21, 0, 0, 216, 217, 7, 16, 0, 0, 217, + 218, 7, 0, 0, 0, 218, 219, 7, 11, 0, 0, 219, 221, 7, 3, 0, 0, 220, 211, + 1, 0, 0, 0, 220, 215, 1, 0, 0, 0, 221, 56, 1, 0, 0, 0, 222, 223, 7, 22, + 0, 0, 223, 58, 1, 0, 0, 0, 224, 226, 3, 57, 28, 0, 225, 224, 1, 0, 0, 0, + 225, 226, 1, 0, 0, 0, 226, 228, 1, 0, 0, 0, 227, 229, 3, 73, 36, 0, 228, + 227, 1, 0, 0, 0, 229, 230, 1, 0, 0, 0, 230, 228, 1, 0, 0, 0, 230, 231, + 1, 0, 0, 0, 231, 239, 1, 0, 0, 0, 232, 236, 5, 46, 0, 0, 233, 235, 3, 73, + 36, 0, 234, 233, 1, 0, 0, 0, 235, 238, 1, 0, 0, 0, 236, 234, 1, 0, 0, 0, + 236, 237, 1, 0, 0, 0, 237, 240, 1, 0, 0, 0, 238, 236, 1, 0, 0, 0, 239, + 232, 1, 0, 0, 0, 239, 240, 1, 0, 0, 0, 240, 250, 1, 0, 0, 0, 241, 243, + 7, 3, 0, 0, 242, 244, 3, 57, 28, 0, 243, 242, 1, 0, 0, 0, 243, 244, 1, + 0, 0, 0, 244, 246, 1, 0, 0, 0, 245, 247, 3, 73, 36, 0, 246, 245, 1, 0, + 0, 0, 247, 248, 1, 0, 0, 0, 248, 246, 1, 0, 0, 0, 248, 249, 1, 0, 0, 0, + 249, 251, 1, 0, 0, 0, 250, 241, 1, 0, 0, 0, 250, 251, 1, 0, 0, 0, 251, + 273, 1, 0, 0, 0, 252, 254, 3, 57, 28, 0, 253, 252, 1, 0, 0, 0, 253, 254, + 1, 0, 0, 0, 254, 255, 1, 0, 0, 0, 255, 257, 5, 46, 0, 0, 256, 258, 3, 73, + 36, 0, 257, 256, 1, 0, 0, 0, 258, 259, 1, 0, 0, 0, 259, 257, 1, 0, 0, 0, + 259, 260, 1, 0, 0, 0, 260, 270, 1, 0, 0, 0, 261, 263, 7, 3, 0, 0, 262, + 264, 3, 57, 28, 0, 263, 262, 1, 0, 0, 0, 263, 264, 1, 0, 0, 0, 264, 266, + 1, 0, 0, 0, 265, 267, 3, 73, 36, 0, 266, 265, 1, 0, 0, 0, 267, 268, 1, + 0, 0, 0, 268, 266, 1, 0, 0, 0, 268, 269, 1, 0, 0, 0, 269, 271, 1, 0, 0, + 0, 270, 261, 1, 0, 0, 0, 270, 271, 1, 0, 0, 0, 271, 273, 1, 0, 0, 0, 272, + 225, 1, 0, 0, 0, 272, 253, 1, 0, 0, 0, 273, 60, 1, 0, 0, 0, 274, 280, 5, + 34, 0, 0, 275, 279, 8, 23, 0, 0, 276, 277, 5, 92, 0, 0, 277, 279, 9, 0, + 0, 0, 278, 275, 1, 0, 0, 0, 278, 276, 1, 0, 0, 0, 279, 282, 1, 0, 0, 0, + 280, 278, 1, 0, 0, 0, 280, 281, 1, 0, 0, 0, 281, 283, 1, 0, 0, 0, 282, + 280, 1, 0, 0, 0, 283, 295, 5, 34, 0, 0, 284, 290, 5, 39, 0, 0, 285, 289, + 8, 24, 0, 0, 286, 287, 5, 92, 0, 0, 287, 289, 9, 0, 0, 0, 288, 285, 1, + 0, 0, 0, 288, 286, 1, 0, 0, 0, 289, 292, 1, 0, 0, 0, 290, 288, 1, 0, 0, + 0, 290, 291, 1, 0, 0, 0, 291, 293, 1, 0, 0, 0, 292, 290, 1, 0, 0, 0, 293, + 295, 5, 39, 0, 0, 294, 274, 1, 0, 0, 0, 294, 284, 1, 0, 0, 0, 295, 62, + 1, 0, 0, 0, 296, 300, 7, 25, 0, 0, 297, 299, 7, 26, 0, 0, 298, 297, 1, + 0, 0, 0, 299, 302, 1, 0, 0, 0, 300, 298, 1, 0, 0, 0, 300, 301, 1, 0, 0, + 0, 301, 64, 1, 0, 0, 0, 302, 300, 1, 0, 0, 0, 303, 304, 5, 91, 0, 0, 304, + 305, 5, 93, 0, 0, 305, 66, 1, 0, 0, 0, 306, 307, 5, 91, 0, 0, 307, 308, + 5, 42, 0, 0, 308, 309, 5, 93, 0, 0, 309, 68, 1, 0, 0, 0, 310, 317, 3, 63, + 31, 0, 311, 312, 5, 46, 0, 0, 312, 316, 3, 63, 31, 0, 313, 316, 3, 65, + 32, 0, 314, 316, 3, 67, 33, 0, 315, 311, 1, 0, 0, 0, 315, 313, 1, 0, 0, + 0, 315, 314, 1, 0, 0, 0, 316, 319, 1, 0, 0, 0, 317, 315, 1, 0, 0, 0, 317, + 318, 1, 0, 0, 0, 318, 70, 1, 0, 0, 0, 319, 317, 1, 0, 0, 0, 320, 322, 7, + 27, 0, 0, 321, 320, 1, 0, 0, 0, 322, 323, 1, 0, 0, 0, 323, 321, 1, 0, 0, + 0, 323, 324, 1, 0, 0, 0, 324, 325, 1, 0, 0, 0, 325, 326, 6, 35, 0, 0, 326, + 72, 1, 0, 0, 0, 327, 328, 7, 28, 0, 0, 328, 74, 1, 0, 0, 0, 329, 331, 8, + 29, 0, 0, 330, 329, 1, 0, 0, 0, 331, 332, 1, 0, 0, 0, 332, 330, 1, 0, 0, + 0, 332, 333, 1, 0, 0, 0, 333, 76, 1, 0, 0, 0, 30, 0, 90, 119, 138, 160, + 177, 220, 225, 230, 236, 239, 243, 248, 250, 253, 259, 263, 268, 270, 272, + 278, 280, 288, 290, 294, 300, 315, 317, 323, 332, 1, 6, 0, 0, } deserializer := antlr.NewATNDeserializer(nil) staticData.atn = deserializer.Deserialize(staticData.serializedATN) diff --git a/pkg/parser/grammar/filterquery_parser.go b/pkg/parser/grammar/filterquery_parser.go index aa8285b63c..dfdc007568 100644 --- a/pkg/parser/grammar/filterquery_parser.go +++ b/pkg/parser/grammar/filterquery_parser.go @@ -51,97 +51,97 @@ func filterqueryParserInit() { } staticData.PredictionContextCache = antlr.NewPredictionContextCache() staticData.serializedATN = []int32{ - 4, 1, 33, 212, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, + 4, 1, 33, 213, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 5, 2, 43, 8, 2, 10, 2, 12, 2, 46, 9, 2, 1, 3, 1, 3, 1, 3, 1, 3, 5, 3, 52, 8, 3, 10, 3, 12, 3, 55, 9, 3, 1, 4, 3, 4, 58, 8, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, - 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 70, 8, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, + 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 71, 8, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, - 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, - 6, 148, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, - 7, 3, 7, 160, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, - 8, 1, 8, 1, 8, 1, 8, 3, 8, 174, 8, 8, 1, 9, 1, 9, 1, 9, 5, 9, 179, 8, 9, - 10, 9, 12, 9, 182, 9, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, - 1, 12, 1, 12, 1, 12, 5, 12, 194, 8, 12, 10, 12, 12, 12, 197, 9, 12, 1, - 13, 1, 13, 1, 13, 3, 13, 202, 8, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, - 1, 15, 1, 16, 1, 16, 1, 16, 0, 0, 17, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, - 20, 22, 24, 26, 28, 30, 32, 0, 6, 1, 0, 7, 8, 2, 0, 13, 13, 15, 15, 2, - 0, 14, 14, 16, 16, 2, 0, 30, 30, 33, 33, 1, 0, 25, 27, 1, 0, 28, 31, 225, - 0, 34, 1, 0, 0, 0, 2, 37, 1, 0, 0, 0, 4, 39, 1, 0, 0, 0, 6, 47, 1, 0, 0, - 0, 8, 57, 1, 0, 0, 0, 10, 69, 1, 0, 0, 0, 12, 147, 1, 0, 0, 0, 14, 159, - 1, 0, 0, 0, 16, 173, 1, 0, 0, 0, 18, 175, 1, 0, 0, 0, 20, 183, 1, 0, 0, - 0, 22, 185, 1, 0, 0, 0, 24, 190, 1, 0, 0, 0, 26, 201, 1, 0, 0, 0, 28, 203, - 1, 0, 0, 0, 30, 207, 1, 0, 0, 0, 32, 209, 1, 0, 0, 0, 34, 35, 3, 2, 1, - 0, 35, 36, 5, 0, 0, 1, 36, 1, 1, 0, 0, 0, 37, 38, 3, 4, 2, 0, 38, 3, 1, - 0, 0, 0, 39, 44, 3, 6, 3, 0, 40, 41, 5, 24, 0, 0, 41, 43, 3, 6, 3, 0, 42, - 40, 1, 0, 0, 0, 43, 46, 1, 0, 0, 0, 44, 42, 1, 0, 0, 0, 44, 45, 1, 0, 0, - 0, 45, 5, 1, 0, 0, 0, 46, 44, 1, 0, 0, 0, 47, 53, 3, 8, 4, 0, 48, 49, 5, - 23, 0, 0, 49, 52, 3, 8, 4, 0, 50, 52, 3, 8, 4, 0, 51, 48, 1, 0, 0, 0, 51, - 50, 1, 0, 0, 0, 52, 55, 1, 0, 0, 0, 53, 51, 1, 0, 0, 0, 53, 54, 1, 0, 0, - 0, 54, 7, 1, 0, 0, 0, 55, 53, 1, 0, 0, 0, 56, 58, 5, 22, 0, 0, 57, 56, - 1, 0, 0, 0, 57, 58, 1, 0, 0, 0, 58, 59, 1, 0, 0, 0, 59, 60, 3, 10, 5, 0, - 60, 9, 1, 0, 0, 0, 61, 62, 5, 1, 0, 0, 62, 63, 3, 4, 2, 0, 63, 64, 5, 2, - 0, 0, 64, 70, 1, 0, 0, 0, 65, 70, 3, 12, 6, 0, 66, 70, 3, 22, 11, 0, 67, - 70, 3, 20, 10, 0, 68, 70, 3, 32, 16, 0, 69, 61, 1, 0, 0, 0, 69, 65, 1, - 0, 0, 0, 69, 66, 1, 0, 0, 0, 69, 67, 1, 0, 0, 0, 69, 68, 1, 0, 0, 0, 70, - 11, 1, 0, 0, 0, 71, 72, 3, 32, 16, 0, 72, 73, 5, 6, 0, 0, 73, 74, 3, 30, - 15, 0, 74, 148, 1, 0, 0, 0, 75, 76, 3, 32, 16, 0, 76, 77, 7, 0, 0, 0, 77, - 78, 3, 30, 15, 0, 78, 148, 1, 0, 0, 0, 79, 80, 3, 32, 16, 0, 80, 81, 5, - 9, 0, 0, 81, 82, 3, 30, 15, 0, 82, 148, 1, 0, 0, 0, 83, 84, 3, 32, 16, - 0, 84, 85, 5, 10, 0, 0, 85, 86, 3, 30, 15, 0, 86, 148, 1, 0, 0, 0, 87, - 88, 3, 32, 16, 0, 88, 89, 5, 11, 0, 0, 89, 90, 3, 30, 15, 0, 90, 148, 1, - 0, 0, 0, 91, 92, 3, 32, 16, 0, 92, 93, 5, 12, 0, 0, 93, 94, 3, 30, 15, - 0, 94, 148, 1, 0, 0, 0, 95, 96, 3, 32, 16, 0, 96, 97, 7, 1, 0, 0, 97, 98, - 3, 30, 15, 0, 98, 148, 1, 0, 0, 0, 99, 100, 3, 32, 16, 0, 100, 101, 7, - 2, 0, 0, 101, 102, 3, 30, 15, 0, 102, 148, 1, 0, 0, 0, 103, 104, 3, 32, - 16, 0, 104, 105, 5, 17, 0, 0, 105, 106, 3, 30, 15, 0, 106, 107, 5, 23, - 0, 0, 107, 108, 3, 30, 15, 0, 108, 148, 1, 0, 0, 0, 109, 110, 3, 32, 16, - 0, 110, 111, 5, 22, 0, 0, 111, 112, 5, 17, 0, 0, 112, 113, 3, 30, 15, 0, - 113, 114, 5, 23, 0, 0, 114, 115, 3, 30, 15, 0, 115, 148, 1, 0, 0, 0, 116, - 117, 3, 32, 16, 0, 117, 118, 3, 14, 7, 0, 118, 148, 1, 0, 0, 0, 119, 120, - 3, 32, 16, 0, 120, 121, 3, 16, 8, 0, 121, 148, 1, 0, 0, 0, 122, 123, 3, - 32, 16, 0, 123, 124, 5, 18, 0, 0, 124, 148, 1, 0, 0, 0, 125, 126, 3, 32, - 16, 0, 126, 127, 5, 22, 0, 0, 127, 128, 5, 18, 0, 0, 128, 148, 1, 0, 0, - 0, 129, 130, 3, 32, 16, 0, 130, 131, 5, 19, 0, 0, 131, 132, 3, 30, 15, - 0, 132, 148, 1, 0, 0, 0, 133, 134, 3, 32, 16, 0, 134, 135, 5, 22, 0, 0, - 135, 136, 5, 19, 0, 0, 136, 137, 3, 30, 15, 0, 137, 148, 1, 0, 0, 0, 138, - 139, 3, 32, 16, 0, 139, 140, 5, 20, 0, 0, 140, 141, 3, 30, 15, 0, 141, - 148, 1, 0, 0, 0, 142, 143, 3, 32, 16, 0, 143, 144, 5, 22, 0, 0, 144, 145, - 5, 20, 0, 0, 145, 146, 3, 30, 15, 0, 146, 148, 1, 0, 0, 0, 147, 71, 1, - 0, 0, 0, 147, 75, 1, 0, 0, 0, 147, 79, 1, 0, 0, 0, 147, 83, 1, 0, 0, 0, - 147, 87, 1, 0, 0, 0, 147, 91, 1, 0, 0, 0, 147, 95, 1, 0, 0, 0, 147, 99, - 1, 0, 0, 0, 147, 103, 1, 0, 0, 0, 147, 109, 1, 0, 0, 0, 147, 116, 1, 0, - 0, 0, 147, 119, 1, 0, 0, 0, 147, 122, 1, 0, 0, 0, 147, 125, 1, 0, 0, 0, - 147, 129, 1, 0, 0, 0, 147, 133, 1, 0, 0, 0, 147, 138, 1, 0, 0, 0, 147, - 142, 1, 0, 0, 0, 148, 13, 1, 0, 0, 0, 149, 150, 5, 21, 0, 0, 150, 151, - 5, 1, 0, 0, 151, 152, 3, 18, 9, 0, 152, 153, 5, 2, 0, 0, 153, 160, 1, 0, - 0, 0, 154, 155, 5, 21, 0, 0, 155, 156, 5, 3, 0, 0, 156, 157, 3, 18, 9, - 0, 157, 158, 5, 4, 0, 0, 158, 160, 1, 0, 0, 0, 159, 149, 1, 0, 0, 0, 159, - 154, 1, 0, 0, 0, 160, 15, 1, 0, 0, 0, 161, 162, 5, 22, 0, 0, 162, 163, - 5, 21, 0, 0, 163, 164, 5, 1, 0, 0, 164, 165, 3, 18, 9, 0, 165, 166, 5, - 2, 0, 0, 166, 174, 1, 0, 0, 0, 167, 168, 5, 22, 0, 0, 168, 169, 5, 21, - 0, 0, 169, 170, 5, 3, 0, 0, 170, 171, 3, 18, 9, 0, 171, 172, 5, 4, 0, 0, - 172, 174, 1, 0, 0, 0, 173, 161, 1, 0, 0, 0, 173, 167, 1, 0, 0, 0, 174, - 17, 1, 0, 0, 0, 175, 180, 3, 30, 15, 0, 176, 177, 5, 5, 0, 0, 177, 179, - 3, 30, 15, 0, 178, 176, 1, 0, 0, 0, 179, 182, 1, 0, 0, 0, 180, 178, 1, - 0, 0, 0, 180, 181, 1, 0, 0, 0, 181, 19, 1, 0, 0, 0, 182, 180, 1, 0, 0, - 0, 183, 184, 7, 3, 0, 0, 184, 21, 1, 0, 0, 0, 185, 186, 7, 4, 0, 0, 186, - 187, 5, 1, 0, 0, 187, 188, 3, 24, 12, 0, 188, 189, 5, 2, 0, 0, 189, 23, - 1, 0, 0, 0, 190, 195, 3, 26, 13, 0, 191, 192, 5, 5, 0, 0, 192, 194, 3, - 26, 13, 0, 193, 191, 1, 0, 0, 0, 194, 197, 1, 0, 0, 0, 195, 193, 1, 0, - 0, 0, 195, 196, 1, 0, 0, 0, 196, 25, 1, 0, 0, 0, 197, 195, 1, 0, 0, 0, - 198, 202, 3, 32, 16, 0, 199, 202, 3, 30, 15, 0, 200, 202, 3, 28, 14, 0, - 201, 198, 1, 0, 0, 0, 201, 199, 1, 0, 0, 0, 201, 200, 1, 0, 0, 0, 202, - 27, 1, 0, 0, 0, 203, 204, 5, 3, 0, 0, 204, 205, 3, 18, 9, 0, 205, 206, - 5, 4, 0, 0, 206, 29, 1, 0, 0, 0, 207, 208, 7, 5, 0, 0, 208, 31, 1, 0, 0, - 0, 209, 210, 5, 31, 0, 0, 210, 33, 1, 0, 0, 0, 11, 44, 51, 53, 57, 69, - 147, 159, 173, 180, 195, 201, + 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, + 6, 3, 6, 149, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, + 7, 1, 7, 3, 7, 161, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, + 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 175, 8, 8, 1, 9, 1, 9, 1, 9, 5, 9, 180, + 8, 9, 10, 9, 12, 9, 183, 9, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, + 1, 11, 1, 12, 1, 12, 1, 12, 5, 12, 195, 8, 12, 10, 12, 12, 12, 198, 9, + 12, 1, 13, 1, 13, 1, 13, 3, 13, 203, 8, 13, 1, 14, 1, 14, 1, 14, 1, 14, + 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 0, 0, 17, 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, 32, 0, 6, 1, 0, 7, 8, 2, 0, 13, 13, 15, + 15, 2, 0, 14, 14, 16, 16, 2, 0, 30, 30, 33, 33, 1, 0, 25, 27, 1, 0, 28, + 31, 227, 0, 34, 1, 0, 0, 0, 2, 37, 1, 0, 0, 0, 4, 39, 1, 0, 0, 0, 6, 47, + 1, 0, 0, 0, 8, 57, 1, 0, 0, 0, 10, 70, 1, 0, 0, 0, 12, 148, 1, 0, 0, 0, + 14, 160, 1, 0, 0, 0, 16, 174, 1, 0, 0, 0, 18, 176, 1, 0, 0, 0, 20, 184, + 1, 0, 0, 0, 22, 186, 1, 0, 0, 0, 24, 191, 1, 0, 0, 0, 26, 202, 1, 0, 0, + 0, 28, 204, 1, 0, 0, 0, 30, 208, 1, 0, 0, 0, 32, 210, 1, 0, 0, 0, 34, 35, + 3, 2, 1, 0, 35, 36, 5, 0, 0, 1, 36, 1, 1, 0, 0, 0, 37, 38, 3, 4, 2, 0, + 38, 3, 1, 0, 0, 0, 39, 44, 3, 6, 3, 0, 40, 41, 5, 24, 0, 0, 41, 43, 3, + 6, 3, 0, 42, 40, 1, 0, 0, 0, 43, 46, 1, 0, 0, 0, 44, 42, 1, 0, 0, 0, 44, + 45, 1, 0, 0, 0, 45, 5, 1, 0, 0, 0, 46, 44, 1, 0, 0, 0, 47, 53, 3, 8, 4, + 0, 48, 49, 5, 23, 0, 0, 49, 52, 3, 8, 4, 0, 50, 52, 3, 8, 4, 0, 51, 48, + 1, 0, 0, 0, 51, 50, 1, 0, 0, 0, 52, 55, 1, 0, 0, 0, 53, 51, 1, 0, 0, 0, + 53, 54, 1, 0, 0, 0, 54, 7, 1, 0, 0, 0, 55, 53, 1, 0, 0, 0, 56, 58, 5, 22, + 0, 0, 57, 56, 1, 0, 0, 0, 57, 58, 1, 0, 0, 0, 58, 59, 1, 0, 0, 0, 59, 60, + 3, 10, 5, 0, 60, 9, 1, 0, 0, 0, 61, 62, 5, 1, 0, 0, 62, 63, 3, 4, 2, 0, + 63, 64, 5, 2, 0, 0, 64, 71, 1, 0, 0, 0, 65, 71, 3, 12, 6, 0, 66, 71, 3, + 22, 11, 0, 67, 71, 3, 20, 10, 0, 68, 71, 3, 32, 16, 0, 69, 71, 3, 30, 15, + 0, 70, 61, 1, 0, 0, 0, 70, 65, 1, 0, 0, 0, 70, 66, 1, 0, 0, 0, 70, 67, + 1, 0, 0, 0, 70, 68, 1, 0, 0, 0, 70, 69, 1, 0, 0, 0, 71, 11, 1, 0, 0, 0, + 72, 73, 3, 32, 16, 0, 73, 74, 5, 6, 0, 0, 74, 75, 3, 30, 15, 0, 75, 149, + 1, 0, 0, 0, 76, 77, 3, 32, 16, 0, 77, 78, 7, 0, 0, 0, 78, 79, 3, 30, 15, + 0, 79, 149, 1, 0, 0, 0, 80, 81, 3, 32, 16, 0, 81, 82, 5, 9, 0, 0, 82, 83, + 3, 30, 15, 0, 83, 149, 1, 0, 0, 0, 84, 85, 3, 32, 16, 0, 85, 86, 5, 10, + 0, 0, 86, 87, 3, 30, 15, 0, 87, 149, 1, 0, 0, 0, 88, 89, 3, 32, 16, 0, + 89, 90, 5, 11, 0, 0, 90, 91, 3, 30, 15, 0, 91, 149, 1, 0, 0, 0, 92, 93, + 3, 32, 16, 0, 93, 94, 5, 12, 0, 0, 94, 95, 3, 30, 15, 0, 95, 149, 1, 0, + 0, 0, 96, 97, 3, 32, 16, 0, 97, 98, 7, 1, 0, 0, 98, 99, 3, 30, 15, 0, 99, + 149, 1, 0, 0, 0, 100, 101, 3, 32, 16, 0, 101, 102, 7, 2, 0, 0, 102, 103, + 3, 30, 15, 0, 103, 149, 1, 0, 0, 0, 104, 105, 3, 32, 16, 0, 105, 106, 5, + 17, 0, 0, 106, 107, 3, 30, 15, 0, 107, 108, 5, 23, 0, 0, 108, 109, 3, 30, + 15, 0, 109, 149, 1, 0, 0, 0, 110, 111, 3, 32, 16, 0, 111, 112, 5, 22, 0, + 0, 112, 113, 5, 17, 0, 0, 113, 114, 3, 30, 15, 0, 114, 115, 5, 23, 0, 0, + 115, 116, 3, 30, 15, 0, 116, 149, 1, 0, 0, 0, 117, 118, 3, 32, 16, 0, 118, + 119, 3, 14, 7, 0, 119, 149, 1, 0, 0, 0, 120, 121, 3, 32, 16, 0, 121, 122, + 3, 16, 8, 0, 122, 149, 1, 0, 0, 0, 123, 124, 3, 32, 16, 0, 124, 125, 5, + 18, 0, 0, 125, 149, 1, 0, 0, 0, 126, 127, 3, 32, 16, 0, 127, 128, 5, 22, + 0, 0, 128, 129, 5, 18, 0, 0, 129, 149, 1, 0, 0, 0, 130, 131, 3, 32, 16, + 0, 131, 132, 5, 19, 0, 0, 132, 133, 3, 30, 15, 0, 133, 149, 1, 0, 0, 0, + 134, 135, 3, 32, 16, 0, 135, 136, 5, 22, 0, 0, 136, 137, 5, 19, 0, 0, 137, + 138, 3, 30, 15, 0, 138, 149, 1, 0, 0, 0, 139, 140, 3, 32, 16, 0, 140, 141, + 5, 20, 0, 0, 141, 142, 3, 30, 15, 0, 142, 149, 1, 0, 0, 0, 143, 144, 3, + 32, 16, 0, 144, 145, 5, 22, 0, 0, 145, 146, 5, 20, 0, 0, 146, 147, 3, 30, + 15, 0, 147, 149, 1, 0, 0, 0, 148, 72, 1, 0, 0, 0, 148, 76, 1, 0, 0, 0, + 148, 80, 1, 0, 0, 0, 148, 84, 1, 0, 0, 0, 148, 88, 1, 0, 0, 0, 148, 92, + 1, 0, 0, 0, 148, 96, 1, 0, 0, 0, 148, 100, 1, 0, 0, 0, 148, 104, 1, 0, + 0, 0, 148, 110, 1, 0, 0, 0, 148, 117, 1, 0, 0, 0, 148, 120, 1, 0, 0, 0, + 148, 123, 1, 0, 0, 0, 148, 126, 1, 0, 0, 0, 148, 130, 1, 0, 0, 0, 148, + 134, 1, 0, 0, 0, 148, 139, 1, 0, 0, 0, 148, 143, 1, 0, 0, 0, 149, 13, 1, + 0, 0, 0, 150, 151, 5, 21, 0, 0, 151, 152, 5, 1, 0, 0, 152, 153, 3, 18, + 9, 0, 153, 154, 5, 2, 0, 0, 154, 161, 1, 0, 0, 0, 155, 156, 5, 21, 0, 0, + 156, 157, 5, 3, 0, 0, 157, 158, 3, 18, 9, 0, 158, 159, 5, 4, 0, 0, 159, + 161, 1, 0, 0, 0, 160, 150, 1, 0, 0, 0, 160, 155, 1, 0, 0, 0, 161, 15, 1, + 0, 0, 0, 162, 163, 5, 22, 0, 0, 163, 164, 5, 21, 0, 0, 164, 165, 5, 1, + 0, 0, 165, 166, 3, 18, 9, 0, 166, 167, 5, 2, 0, 0, 167, 175, 1, 0, 0, 0, + 168, 169, 5, 22, 0, 0, 169, 170, 5, 21, 0, 0, 170, 171, 5, 3, 0, 0, 171, + 172, 3, 18, 9, 0, 172, 173, 5, 4, 0, 0, 173, 175, 1, 0, 0, 0, 174, 162, + 1, 0, 0, 0, 174, 168, 1, 0, 0, 0, 175, 17, 1, 0, 0, 0, 176, 181, 3, 30, + 15, 0, 177, 178, 5, 5, 0, 0, 178, 180, 3, 30, 15, 0, 179, 177, 1, 0, 0, + 0, 180, 183, 1, 0, 0, 0, 181, 179, 1, 0, 0, 0, 181, 182, 1, 0, 0, 0, 182, + 19, 1, 0, 0, 0, 183, 181, 1, 0, 0, 0, 184, 185, 7, 3, 0, 0, 185, 21, 1, + 0, 0, 0, 186, 187, 7, 4, 0, 0, 187, 188, 5, 1, 0, 0, 188, 189, 3, 24, 12, + 0, 189, 190, 5, 2, 0, 0, 190, 23, 1, 0, 0, 0, 191, 196, 3, 26, 13, 0, 192, + 193, 5, 5, 0, 0, 193, 195, 3, 26, 13, 0, 194, 192, 1, 0, 0, 0, 195, 198, + 1, 0, 0, 0, 196, 194, 1, 0, 0, 0, 196, 197, 1, 0, 0, 0, 197, 25, 1, 0, + 0, 0, 198, 196, 1, 0, 0, 0, 199, 203, 3, 32, 16, 0, 200, 203, 3, 30, 15, + 0, 201, 203, 3, 28, 14, 0, 202, 199, 1, 0, 0, 0, 202, 200, 1, 0, 0, 0, + 202, 201, 1, 0, 0, 0, 203, 27, 1, 0, 0, 0, 204, 205, 5, 3, 0, 0, 205, 206, + 3, 18, 9, 0, 206, 207, 5, 4, 0, 0, 207, 29, 1, 0, 0, 0, 208, 209, 7, 5, + 0, 0, 209, 31, 1, 0, 0, 0, 210, 211, 5, 31, 0, 0, 211, 33, 1, 0, 0, 0, + 11, 44, 51, 53, 57, 70, 148, 160, 174, 181, 196, 202, } deserializer := antlr.NewATNDeserializer(nil) staticData.atn = deserializer.Deserialize(staticData.serializedATN) @@ -802,7 +802,7 @@ func (p *FilterQueryParser) AndExpression() (localctx IAndExpressionContext) { } _la = p.GetTokenStream().LA(1) - for (int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&12058624002) != 0 { + for (int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&12863930370) != 0 { p.SetState(51) p.GetErrorHandler().Sync(p) if p.HasError() { @@ -824,7 +824,7 @@ func (p *FilterQueryParser) AndExpression() (localctx IAndExpressionContext) { p.UnaryExpression() } - case FilterQueryParserLPAREN, FilterQueryParserNOT, FilterQueryParserHAS, FilterQueryParserHASANY, FilterQueryParserHASALL, FilterQueryParserQUOTED_TEXT, FilterQueryParserKEY, FilterQueryParserFREETEXT: + case FilterQueryParserLPAREN, FilterQueryParserNOT, FilterQueryParserHAS, FilterQueryParserHASANY, FilterQueryParserHASALL, FilterQueryParserBOOL, FilterQueryParserNUMBER, FilterQueryParserQUOTED_TEXT, FilterQueryParserKEY, FilterQueryParserFREETEXT: { p.SetState(50) p.UnaryExpression() @@ -1010,6 +1010,7 @@ type IPrimaryContext interface { FunctionCall() IFunctionCallContext FullText() IFullTextContext Key() IKeyContext + Value() IValueContext // IsPrimaryContext differentiates from other interfaces. IsPrimaryContext() @@ -1135,6 +1136,22 @@ func (s *PrimaryContext) Key() IKeyContext { return t.(IKeyContext) } +func (s *PrimaryContext) Value() IValueContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IValueContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IValueContext) +} + func (s *PrimaryContext) GetRuleContext() antlr.RuleContext { return s } @@ -1168,7 +1185,7 @@ func (s *PrimaryContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { func (p *FilterQueryParser) Primary() (localctx IPrimaryContext) { localctx = NewPrimaryContext(p, p.GetParserRuleContext(), p.GetState()) p.EnterRule(localctx, 10, FilterQueryParserRULE_primary) - p.SetState(69) + p.SetState(70) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -1226,6 +1243,13 @@ func (p *FilterQueryParser) Primary() (localctx IPrimaryContext) { p.Key() } + case 6: + p.EnterOuterAlt(localctx, 6) + { + p.SetState(69) + p.Value() + } + case antlr.ATNInvalidAltNumber: goto errorExit } @@ -1502,7 +1526,7 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { p.EnterRule(localctx, 12, FilterQueryParserRULE_comparison) var _la int - p.SetState(147) + p.SetState(148) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -1512,11 +1536,11 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { case 1: p.EnterOuterAlt(localctx, 1) { - p.SetState(71) + p.SetState(72) p.Key() } { - p.SetState(72) + p.SetState(73) p.Match(FilterQueryParserEQUALS) if p.HasError() { // Recognition error - abort rule @@ -1524,18 +1548,18 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { } } { - p.SetState(73) + p.SetState(74) p.Value() } case 2: p.EnterOuterAlt(localctx, 2) { - p.SetState(75) + p.SetState(76) p.Key() } { - p.SetState(76) + p.SetState(77) _la = p.GetTokenStream().LA(1) if !(_la == FilterQueryParserNOT_EQUALS || _la == FilterQueryParserNEQ) { @@ -1546,18 +1570,18 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { } } { - p.SetState(77) + p.SetState(78) p.Value() } case 3: p.EnterOuterAlt(localctx, 3) { - p.SetState(79) + p.SetState(80) p.Key() } { - p.SetState(80) + p.SetState(81) p.Match(FilterQueryParserLT) if p.HasError() { // Recognition error - abort rule @@ -1565,18 +1589,18 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { } } { - p.SetState(81) + p.SetState(82) p.Value() } case 4: p.EnterOuterAlt(localctx, 4) { - p.SetState(83) + p.SetState(84) p.Key() } { - p.SetState(84) + p.SetState(85) p.Match(FilterQueryParserLE) if p.HasError() { // Recognition error - abort rule @@ -1584,18 +1608,18 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { } } { - p.SetState(85) + p.SetState(86) p.Value() } case 5: p.EnterOuterAlt(localctx, 5) { - p.SetState(87) + p.SetState(88) p.Key() } { - p.SetState(88) + p.SetState(89) p.Match(FilterQueryParserGT) if p.HasError() { // Recognition error - abort rule @@ -1603,18 +1627,18 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { } } { - p.SetState(89) + p.SetState(90) p.Value() } case 6: p.EnterOuterAlt(localctx, 6) { - p.SetState(91) + p.SetState(92) p.Key() } { - p.SetState(92) + p.SetState(93) p.Match(FilterQueryParserGE) if p.HasError() { // Recognition error - abort rule @@ -1622,18 +1646,18 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { } } { - p.SetState(93) + p.SetState(94) p.Value() } case 7: p.EnterOuterAlt(localctx, 7) { - p.SetState(95) + p.SetState(96) p.Key() } { - p.SetState(96) + p.SetState(97) _la = p.GetTokenStream().LA(1) if !(_la == FilterQueryParserLIKE || _la == FilterQueryParserILIKE) { @@ -1644,18 +1668,18 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { } } { - p.SetState(97) + p.SetState(98) p.Value() } case 8: p.EnterOuterAlt(localctx, 8) { - p.SetState(99) + p.SetState(100) p.Key() } { - p.SetState(100) + p.SetState(101) _la = p.GetTokenStream().LA(1) if !(_la == FilterQueryParserNOT_LIKE || _la == FilterQueryParserNOT_ILIKE) { @@ -1666,18 +1690,18 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { } } { - p.SetState(101) + p.SetState(102) p.Value() } case 9: p.EnterOuterAlt(localctx, 9) { - p.SetState(103) + p.SetState(104) p.Key() } { - p.SetState(104) + p.SetState(105) p.Match(FilterQueryParserBETWEEN) if p.HasError() { // Recognition error - abort rule @@ -1685,11 +1709,11 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { } } { - p.SetState(105) + p.SetState(106) p.Value() } { - p.SetState(106) + p.SetState(107) p.Match(FilterQueryParserAND) if p.HasError() { // Recognition error - abort rule @@ -1697,18 +1721,18 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { } } { - p.SetState(107) + p.SetState(108) p.Value() } case 10: p.EnterOuterAlt(localctx, 10) { - p.SetState(109) + p.SetState(110) p.Key() } { - p.SetState(110) + p.SetState(111) p.Match(FilterQueryParserNOT) if p.HasError() { // Recognition error - abort rule @@ -1716,7 +1740,7 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { } } { - p.SetState(111) + p.SetState(112) p.Match(FilterQueryParserBETWEEN) if p.HasError() { // Recognition error - abort rule @@ -1724,11 +1748,11 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { } } { - p.SetState(112) + p.SetState(113) p.Value() } { - p.SetState(113) + p.SetState(114) p.Match(FilterQueryParserAND) if p.HasError() { // Recognition error - abort rule @@ -1736,40 +1760,40 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { } } { - p.SetState(114) + p.SetState(115) p.Value() } case 11: p.EnterOuterAlt(localctx, 11) { - p.SetState(116) + p.SetState(117) p.Key() } { - p.SetState(117) + p.SetState(118) p.InClause() } case 12: p.EnterOuterAlt(localctx, 12) { - p.SetState(119) + p.SetState(120) p.Key() } { - p.SetState(120) + p.SetState(121) p.NotInClause() } case 13: p.EnterOuterAlt(localctx, 13) { - p.SetState(122) + p.SetState(123) p.Key() } { - p.SetState(123) + p.SetState(124) p.Match(FilterQueryParserEXISTS) if p.HasError() { // Recognition error - abort rule @@ -1780,11 +1804,11 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { case 14: p.EnterOuterAlt(localctx, 14) { - p.SetState(125) + p.SetState(126) p.Key() } { - p.SetState(126) + p.SetState(127) p.Match(FilterQueryParserNOT) if p.HasError() { // Recognition error - abort rule @@ -1792,7 +1816,7 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { } } { - p.SetState(127) + p.SetState(128) p.Match(FilterQueryParserEXISTS) if p.HasError() { // Recognition error - abort rule @@ -1803,11 +1827,11 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { case 15: p.EnterOuterAlt(localctx, 15) { - p.SetState(129) + p.SetState(130) p.Key() } { - p.SetState(130) + p.SetState(131) p.Match(FilterQueryParserREGEXP) if p.HasError() { // Recognition error - abort rule @@ -1815,27 +1839,19 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { } } { - p.SetState(131) + p.SetState(132) p.Value() } case 16: p.EnterOuterAlt(localctx, 16) { - p.SetState(133) + p.SetState(134) p.Key() } - { - p.SetState(134) - p.Match(FilterQueryParserNOT) - if p.HasError() { - // Recognition error - abort rule - goto errorExit - } - } { p.SetState(135) - p.Match(FilterQueryParserREGEXP) + p.Match(FilterQueryParserNOT) if p.HasError() { // Recognition error - abort rule goto errorExit @@ -1843,17 +1859,25 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { } { p.SetState(136) + p.Match(FilterQueryParserREGEXP) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(137) p.Value() } case 17: p.EnterOuterAlt(localctx, 17) { - p.SetState(138) + p.SetState(139) p.Key() } { - p.SetState(139) + p.SetState(140) p.Match(FilterQueryParserCONTAINS) if p.HasError() { // Recognition error - abort rule @@ -1861,18 +1885,18 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { } } { - p.SetState(140) + p.SetState(141) p.Value() } case 18: p.EnterOuterAlt(localctx, 18) { - p.SetState(142) + p.SetState(143) p.Key() } { - p.SetState(143) + p.SetState(144) p.Match(FilterQueryParserNOT) if p.HasError() { // Recognition error - abort rule @@ -1880,7 +1904,7 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { } } { - p.SetState(144) + p.SetState(145) p.Match(FilterQueryParserCONTAINS) if p.HasError() { // Recognition error - abort rule @@ -1888,7 +1912,7 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) { } } { - p.SetState(145) + p.SetState(146) p.Value() } @@ -2029,7 +2053,7 @@ func (s *InClauseContext) Accept(visitor antlr.ParseTreeVisitor) interface{} { func (p *FilterQueryParser) InClause() (localctx IInClauseContext) { localctx = NewInClauseContext(p, p.GetParserRuleContext(), p.GetState()) p.EnterRule(localctx, 14, FilterQueryParserRULE_inClause) - p.SetState(159) + p.SetState(160) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -2039,7 +2063,7 @@ func (p *FilterQueryParser) InClause() (localctx IInClauseContext) { case 1: p.EnterOuterAlt(localctx, 1) { - p.SetState(149) + p.SetState(150) p.Match(FilterQueryParserIN) if p.HasError() { // Recognition error - abort rule @@ -2047,7 +2071,7 @@ func (p *FilterQueryParser) InClause() (localctx IInClauseContext) { } } { - p.SetState(150) + p.SetState(151) p.Match(FilterQueryParserLPAREN) if p.HasError() { // Recognition error - abort rule @@ -2055,11 +2079,11 @@ func (p *FilterQueryParser) InClause() (localctx IInClauseContext) { } } { - p.SetState(151) + p.SetState(152) p.ValueList() } { - p.SetState(152) + p.SetState(153) p.Match(FilterQueryParserRPAREN) if p.HasError() { // Recognition error - abort rule @@ -2070,7 +2094,7 @@ func (p *FilterQueryParser) InClause() (localctx IInClauseContext) { case 2: p.EnterOuterAlt(localctx, 2) { - p.SetState(154) + p.SetState(155) p.Match(FilterQueryParserIN) if p.HasError() { // Recognition error - abort rule @@ -2078,7 +2102,7 @@ func (p *FilterQueryParser) InClause() (localctx IInClauseContext) { } } { - p.SetState(155) + p.SetState(156) p.Match(FilterQueryParserLBRACK) if p.HasError() { // Recognition error - abort rule @@ -2086,11 +2110,11 @@ func (p *FilterQueryParser) InClause() (localctx IInClauseContext) { } } { - p.SetState(156) + p.SetState(157) p.ValueList() } { - p.SetState(157) + p.SetState(158) p.Match(FilterQueryParserRBRACK) if p.HasError() { // Recognition error - abort rule @@ -2240,7 +2264,7 @@ func (s *NotInClauseContext) Accept(visitor antlr.ParseTreeVisitor) interface{} func (p *FilterQueryParser) NotInClause() (localctx INotInClauseContext) { localctx = NewNotInClauseContext(p, p.GetParserRuleContext(), p.GetState()) p.EnterRule(localctx, 16, FilterQueryParserRULE_notInClause) - p.SetState(173) + p.SetState(174) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -2250,7 +2274,7 @@ func (p *FilterQueryParser) NotInClause() (localctx INotInClauseContext) { case 1: p.EnterOuterAlt(localctx, 1) { - p.SetState(161) + p.SetState(162) p.Match(FilterQueryParserNOT) if p.HasError() { // Recognition error - abort rule @@ -2258,7 +2282,7 @@ func (p *FilterQueryParser) NotInClause() (localctx INotInClauseContext) { } } { - p.SetState(162) + p.SetState(163) p.Match(FilterQueryParserIN) if p.HasError() { // Recognition error - abort rule @@ -2266,7 +2290,7 @@ func (p *FilterQueryParser) NotInClause() (localctx INotInClauseContext) { } } { - p.SetState(163) + p.SetState(164) p.Match(FilterQueryParserLPAREN) if p.HasError() { // Recognition error - abort rule @@ -2274,11 +2298,11 @@ func (p *FilterQueryParser) NotInClause() (localctx INotInClauseContext) { } } { - p.SetState(164) + p.SetState(165) p.ValueList() } { - p.SetState(165) + p.SetState(166) p.Match(FilterQueryParserRPAREN) if p.HasError() { // Recognition error - abort rule @@ -2289,7 +2313,7 @@ func (p *FilterQueryParser) NotInClause() (localctx INotInClauseContext) { case 2: p.EnterOuterAlt(localctx, 2) { - p.SetState(167) + p.SetState(168) p.Match(FilterQueryParserNOT) if p.HasError() { // Recognition error - abort rule @@ -2297,7 +2321,7 @@ func (p *FilterQueryParser) NotInClause() (localctx INotInClauseContext) { } } { - p.SetState(168) + p.SetState(169) p.Match(FilterQueryParserIN) if p.HasError() { // Recognition error - abort rule @@ -2305,7 +2329,7 @@ func (p *FilterQueryParser) NotInClause() (localctx INotInClauseContext) { } } { - p.SetState(169) + p.SetState(170) p.Match(FilterQueryParserLBRACK) if p.HasError() { // Recognition error - abort rule @@ -2313,11 +2337,11 @@ func (p *FilterQueryParser) NotInClause() (localctx INotInClauseContext) { } } { - p.SetState(170) + p.SetState(171) p.ValueList() } { - p.SetState(171) + p.SetState(172) p.Match(FilterQueryParserRBRACK) if p.HasError() { // Recognition error - abort rule @@ -2477,10 +2501,10 @@ func (p *FilterQueryParser) ValueList() (localctx IValueListContext) { p.EnterOuterAlt(localctx, 1) { - p.SetState(175) + p.SetState(176) p.Value() } - p.SetState(180) + p.SetState(181) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -2489,7 +2513,7 @@ func (p *FilterQueryParser) ValueList() (localctx IValueListContext) { for _la == FilterQueryParserCOMMA { { - p.SetState(176) + p.SetState(177) p.Match(FilterQueryParserCOMMA) if p.HasError() { // Recognition error - abort rule @@ -2497,11 +2521,11 @@ func (p *FilterQueryParser) ValueList() (localctx IValueListContext) { } } { - p.SetState(177) + p.SetState(178) p.Value() } - p.SetState(182) + p.SetState(183) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -2614,7 +2638,7 @@ func (p *FilterQueryParser) FullText() (localctx IFullTextContext) { p.EnterOuterAlt(localctx, 1) { - p.SetState(183) + p.SetState(184) _la = p.GetTokenStream().LA(1) if !(_la == FilterQueryParserQUOTED_TEXT || _la == FilterQueryParserFREETEXT) { @@ -2762,7 +2786,7 @@ func (p *FilterQueryParser) FunctionCall() (localctx IFunctionCallContext) { p.EnterOuterAlt(localctx, 1) { - p.SetState(185) + p.SetState(186) _la = p.GetTokenStream().LA(1) if !((int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&234881024) != 0) { @@ -2773,7 +2797,7 @@ func (p *FilterQueryParser) FunctionCall() (localctx IFunctionCallContext) { } } { - p.SetState(186) + p.SetState(187) p.Match(FilterQueryParserLPAREN) if p.HasError() { // Recognition error - abort rule @@ -2781,11 +2805,11 @@ func (p *FilterQueryParser) FunctionCall() (localctx IFunctionCallContext) { } } { - p.SetState(187) + p.SetState(188) p.FunctionParamList() } { - p.SetState(188) + p.SetState(189) p.Match(FilterQueryParserRPAREN) if p.HasError() { // Recognition error - abort rule @@ -2941,10 +2965,10 @@ func (p *FilterQueryParser) FunctionParamList() (localctx IFunctionParamListCont p.EnterOuterAlt(localctx, 1) { - p.SetState(190) + p.SetState(191) p.FunctionParam() } - p.SetState(195) + p.SetState(196) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -2953,7 +2977,7 @@ func (p *FilterQueryParser) FunctionParamList() (localctx IFunctionParamListCont for _la == FilterQueryParserCOMMA { { - p.SetState(191) + p.SetState(192) p.Match(FilterQueryParserCOMMA) if p.HasError() { // Recognition error - abort rule @@ -2961,11 +2985,11 @@ func (p *FilterQueryParser) FunctionParamList() (localctx IFunctionParamListCont } } { - p.SetState(192) + p.SetState(193) p.FunctionParam() } - p.SetState(197) + p.SetState(198) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -3115,7 +3139,7 @@ func (s *FunctionParamContext) Accept(visitor antlr.ParseTreeVisitor) interface{ func (p *FilterQueryParser) FunctionParam() (localctx IFunctionParamContext) { localctx = NewFunctionParamContext(p, p.GetParserRuleContext(), p.GetState()) p.EnterRule(localctx, 26, FilterQueryParserRULE_functionParam) - p.SetState(201) + p.SetState(202) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -3125,21 +3149,21 @@ func (p *FilterQueryParser) FunctionParam() (localctx IFunctionParamContext) { case 1: p.EnterOuterAlt(localctx, 1) { - p.SetState(198) + p.SetState(199) p.Key() } case 2: p.EnterOuterAlt(localctx, 2) { - p.SetState(199) + p.SetState(200) p.Value() } case 3: p.EnterOuterAlt(localctx, 3) { - p.SetState(200) + p.SetState(201) p.Array() } @@ -3267,7 +3291,7 @@ func (p *FilterQueryParser) Array() (localctx IArrayContext) { p.EnterRule(localctx, 28, FilterQueryParserRULE_array) p.EnterOuterAlt(localctx, 1) { - p.SetState(203) + p.SetState(204) p.Match(FilterQueryParserLBRACK) if p.HasError() { // Recognition error - abort rule @@ -3275,11 +3299,11 @@ func (p *FilterQueryParser) Array() (localctx IArrayContext) { } } { - p.SetState(204) + p.SetState(205) p.ValueList() } { - p.SetState(205) + p.SetState(206) p.Match(FilterQueryParserRBRACK) if p.HasError() { // Recognition error - abort rule @@ -3402,7 +3426,7 @@ func (p *FilterQueryParser) Value() (localctx IValueContext) { p.EnterOuterAlt(localctx, 1) { - p.SetState(207) + p.SetState(208) _la = p.GetTokenStream().LA(1) if !((int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&4026531840) != 0) { @@ -3511,7 +3535,7 @@ func (p *FilterQueryParser) Key() (localctx IKeyContext) { p.EnterRule(localctx, 32, FilterQueryParserRULE_key) p.EnterOuterAlt(localctx, 1) { - p.SetState(209) + p.SetState(210) p.Match(FilterQueryParserKEY) if p.HasError() { // Recognition error - abort rule diff --git a/pkg/querybuilder/agg_funcs.go b/pkg/querybuilder/agg_funcs.go new file mode 100644 index 0000000000..80279c5a70 --- /dev/null +++ b/pkg/querybuilder/agg_funcs.go @@ -0,0 +1,281 @@ +package querybuilder + +import ( + "github.com/SigNoz/signoz/pkg/valuer" +) + +var ( + AggreFuncMap = map[valuer.String]AggrFunc{} +) + +type AggrFunc struct { + Name valuer.String + FuncName string + Aliases []valuer.String + RequireArgs bool + FuncCombinator bool + Rate bool + MinArgs int + MaxArgs int +} + +var ( + AggrFuncCount = AggrFunc{ + Name: valuer.NewString("count"), + FuncName: "count", + RequireArgs: false, MinArgs: 0, MaxArgs: 1, + } + AggrFuncCountIf = AggrFunc{ + Name: valuer.NewString("countif"), + FuncName: "countIf", + Aliases: []valuer.String{valuer.NewString("count_if")}, + RequireArgs: true, FuncCombinator: true, MinArgs: 1, MaxArgs: 1, + } + AggrFuncCountDistinct = AggrFunc{ + Name: valuer.NewString("countdistinct"), + FuncName: "countDistinct", + Aliases: []valuer.String{valuer.NewString("count_distinct")}, + RequireArgs: true, MinArgs: 1, MaxArgs: 10, + } + AggrFuncCountDistinctIf = AggrFunc{ + Name: valuer.NewString("countdistinctif"), + FuncName: "countDistinctIf", + Aliases: []valuer.String{valuer.NewString("count_distinct_if")}, + RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2, + } + AggrFuncSum = AggrFunc{ + Name: valuer.NewString("sum"), + FuncName: "sum", + RequireArgs: true, MinArgs: 1, MaxArgs: 1, + } + AggrFuncSumIf = AggrFunc{ + Name: valuer.NewString("sumif"), + FuncName: "sumIf", + Aliases: []valuer.String{valuer.NewString("sum_if")}, + RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2, + } + AggrFuncAvg = AggrFunc{ + Name: valuer.NewString("avg"), + FuncName: "avg", + RequireArgs: true, MinArgs: 1, MaxArgs: 1, + } + AggrFuncAvgIf = AggrFunc{ + Name: valuer.NewString("avgif"), + FuncName: "avgIf", + Aliases: []valuer.String{valuer.NewString("avg_if")}, + RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2, + } + AggrFuncMin = AggrFunc{ + Name: valuer.NewString("min"), + FuncName: "min", + RequireArgs: true, MinArgs: 1, MaxArgs: 1, + } + AggrFuncMinIf = AggrFunc{ + Name: valuer.NewString("minif"), + FuncName: "minIf", + Aliases: []valuer.String{valuer.NewString("min_if")}, + RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2, + } + AggrFuncMax = AggrFunc{ + Name: valuer.NewString("max"), + FuncName: "max", + RequireArgs: true, MinArgs: 1, MaxArgs: 1, + } + AggrFuncMaxIf = AggrFunc{ + Name: valuer.NewString("maxif"), + FuncName: "maxIf", + Aliases: []valuer.String{valuer.NewString("max_if")}, + RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2, + } + AggrFuncP05 = AggrFunc{ + Name: valuer.NewString("p05"), + FuncName: "quantile(0.05)", + RequireArgs: true, MinArgs: 1, MaxArgs: 1, + } + AggrFuncP05IF = AggrFunc{ + Name: valuer.NewString("p05if"), + FuncName: "quantileIf(0.05)", + Aliases: []valuer.String{valuer.NewString("p05_if")}, + RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2, + } + AggrFuncP10 = AggrFunc{ + Name: valuer.NewString("p10"), + FuncName: "quantile(0.10)", + RequireArgs: true, MinArgs: 1, MaxArgs: 1, + } + AggrFuncP10IF = AggrFunc{ + Name: valuer.NewString("p10if"), + FuncName: "quantileIf(0.10)", + Aliases: []valuer.String{valuer.NewString("p10_if")}, + RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2, + } + AggrFuncP20 = AggrFunc{ + Name: valuer.NewString("p20"), + FuncName: "quantile(0.20)", + RequireArgs: true, MinArgs: 1, MaxArgs: 1, + } + AggrFuncP20IF = AggrFunc{ + Name: valuer.NewString("p20if"), + FuncName: "quantileIf(0.20)", + Aliases: []valuer.String{valuer.NewString("p20_if")}, + RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2, + } + AggrFuncP25 = AggrFunc{ + Name: valuer.NewString("p25"), + FuncName: "quantile(0.25)", + RequireArgs: true, MinArgs: 1, MaxArgs: 1, + } + AggrFuncP25IF = AggrFunc{ + Name: valuer.NewString("p25if"), + FuncName: "quantileIf(0.25)", + Aliases: []valuer.String{valuer.NewString("p25_if")}, + RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2, + } + AggrFuncP50 = AggrFunc{ + Name: valuer.NewString("p50"), + FuncName: "quantile(0.50)", + RequireArgs: true, MinArgs: 1, MaxArgs: 1, + } + AggrFuncP50IF = AggrFunc{ + Name: valuer.NewString("p50if"), + FuncName: "quantileIf(0.50)", + Aliases: []valuer.String{valuer.NewString("p50_if")}, + RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2, + } + AggrFuncP75 = AggrFunc{ + Name: valuer.NewString("p75"), + FuncName: "quantile(0.75)", + RequireArgs: true, MinArgs: 1, MaxArgs: 1, + } + AggrFuncP75IF = AggrFunc{ + Name: valuer.NewString("p75if"), + FuncName: "quantileIf(0.75)", + Aliases: []valuer.String{valuer.NewString("p75_if")}, + RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2, + } + AggrFuncP90 = AggrFunc{ + Name: valuer.NewString("p90"), + FuncName: "quantile(0.90)", + RequireArgs: true, MinArgs: 1, MaxArgs: 1, + } + AggrFuncP90IF = AggrFunc{ + Name: valuer.NewString("p90if"), + FuncName: "quantileIf(0.90)", + Aliases: []valuer.String{valuer.NewString("p90_if")}, + RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2, + } + AggrFuncP95 = AggrFunc{ + Name: valuer.NewString("p95"), + FuncName: "quantile(0.95)", + RequireArgs: true, MinArgs: 1, MaxArgs: 1, + } + AggrFuncP95IF = AggrFunc{ + Name: valuer.NewString("p95if"), + FuncName: "quantileIf(0.95)", + Aliases: []valuer.String{valuer.NewString("p95_if")}, + RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2, + } + AggrFuncP99 = AggrFunc{ + Name: valuer.NewString("p99"), + FuncName: "quantile(0.99)", + RequireArgs: true, MinArgs: 1, MaxArgs: 1, + } + AggrFuncP99IF = AggrFunc{ + Name: valuer.NewString("p99if"), + FuncName: "quantileIf(0.99)", + Aliases: []valuer.String{valuer.NewString("p99_if")}, + RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2, + } + AggrFuncP999 = AggrFunc{ + Name: valuer.NewString("p999"), + FuncName: "quantile(0.999)", + RequireArgs: true, MinArgs: 1, MaxArgs: 1, + } + AggrFuncP999IF = AggrFunc{ + Name: valuer.NewString("p999if"), + FuncName: "quantileIf(0.999)", + Aliases: []valuer.String{valuer.NewString("p999_if")}, + RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2, + } + AggrFuncRate = AggrFunc{ + Name: valuer.NewString("rate"), + FuncName: "count", + RequireArgs: true, Rate: true, MinArgs: 0, MaxArgs: 1, + } + AggrFuncRateIf = AggrFunc{ + Name: valuer.NewString("rateif"), + FuncName: "count", + Aliases: []valuer.String{valuer.NewString("rate_if")}, + RequireArgs: true, Rate: true, FuncCombinator: true, MinArgs: 1, MaxArgs: 1, + } + AggrFuncRateSum = AggrFunc{ + Name: valuer.NewString("rate_sum"), + FuncName: "sum", + RequireArgs: true, Rate: true, MinArgs: 1, MaxArgs: 1, + } + AggrFuncRateAvg = AggrFunc{ + Name: valuer.NewString("rate_avg"), + FuncName: "avg", + RequireArgs: true, Rate: true, MinArgs: 1, MaxArgs: 1, + } + AggrFuncRateMin = AggrFunc{ + Name: valuer.NewString("rate_min"), + FuncName: "min", + RequireArgs: true, Rate: true, MinArgs: 1, MaxArgs: 1, + } + AggrFuncRateMax = AggrFunc{ + Name: valuer.NewString("rate_max"), + FuncName: "max", + RequireArgs: true, Rate: true, MinArgs: 1, MaxArgs: 1, + } +) + +func init() { + var aggFuncs = []AggrFunc{ + AggrFuncCount, + AggrFuncCountIf, + AggrFuncCountDistinct, + AggrFuncCountDistinctIf, + AggrFuncSum, + AggrFuncSumIf, + AggrFuncAvg, + AggrFuncAvgIf, + AggrFuncMin, + AggrFuncMinIf, + AggrFuncMax, + AggrFuncMaxIf, + AggrFuncP05, + AggrFuncP05IF, + AggrFuncP10, + AggrFuncP10IF, + AggrFuncP20, + AggrFuncP20IF, + AggrFuncP25, + AggrFuncP25IF, + AggrFuncP50, + AggrFuncP50IF, + AggrFuncP75, + AggrFuncP75IF, + AggrFuncP90, + AggrFuncP90IF, + AggrFuncP95, + AggrFuncP95IF, + AggrFuncP99, + AggrFuncP99IF, + AggrFuncP999, + AggrFuncP999IF, + AggrFuncRate, + AggrFuncRateIf, + AggrFuncRateSum, + AggrFuncRateAvg, + AggrFuncRateMin, + AggrFuncRateMax, + } + + for _, aggFunc := range aggFuncs { + AggreFuncMap[aggFunc.Name] = aggFunc + for _, alias := range aggFunc.Aliases { + AggreFuncMap[alias] = aggFunc + } + } +} diff --git a/pkg/querybuilder/agg_rewrite.go b/pkg/querybuilder/agg_rewrite.go new file mode 100644 index 0000000000..ccf0d99040 --- /dev/null +++ b/pkg/querybuilder/agg_rewrite.go @@ -0,0 +1,245 @@ +package querybuilder + +import ( + "context" + "fmt" + "strings" + + chparser "github.com/AfterShip/clickhouse-sql-parser/parser" + "github.com/SigNoz/signoz/pkg/errors" + qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5" + "github.com/SigNoz/signoz/pkg/types/telemetrytypes" + "github.com/SigNoz/signoz/pkg/valuer" + "github.com/huandu/go-sqlbuilder" +) + +type AggExprRewriterOptions struct { + FieldKeys map[string][]*telemetrytypes.TelemetryFieldKey + FullTextColumn *telemetrytypes.TelemetryFieldKey + FieldMapper qbtypes.FieldMapper + ConditionBuilder qbtypes.ConditionBuilder + JsonBodyPrefix string + JsonKeyToKey qbtypes.JsonKeyToFieldFunc + RateInterval uint64 +} + +type aggExprRewriter struct { + opts AggExprRewriterOptions +} + +func NewAggExprRewriter(opts AggExprRewriterOptions) *aggExprRewriter { + return &aggExprRewriter{opts: opts} +} + +// Rewrite parses the given aggregation expression, maps the column, and condition to +// valid data source column and condition expression, and returns the rewritten expression +// and the args if the parametric aggregation function is used. +func (r *aggExprRewriter) Rewrite(expr string) (string, []any, error) { + wrapped := fmt.Sprintf("SELECT %s", expr) + p := chparser.NewParser(wrapped) + stmts, err := p.ParseStmts() + + if err != nil { + return "", nil, errors.WrapInternalf(err, errors.CodeInternal, "failed to parse aggregation expression %q", expr) + } + + if len(stmts) == 0 { + return "", nil, errors.NewInternalf(errors.CodeInternal, "no statements found for %q", expr) + } + + sel, ok := stmts[0].(*chparser.SelectQuery) + if !ok { + return "", nil, errors.NewInternalf(errors.CodeInternal, "expected SelectQuery, got %T", stmts[0]) + } + + if len(sel.SelectItems) == 0 { + return "", nil, errors.NewInternalf(errors.CodeInternal, "no SELECT items for %q", expr) + } + + visitor := newExprVisitor(r.opts.FieldKeys, + r.opts.FullTextColumn, + r.opts.FieldMapper, + r.opts.ConditionBuilder, + r.opts.JsonBodyPrefix, + r.opts.JsonKeyToKey, + ) + // Rewrite the first select item (our expression) + if err := sel.SelectItems[0].Accept(visitor); err != nil { + return "", nil, err + } + // If nothing changed, return original + if !visitor.Modified { + return expr, nil, nil + } + + if visitor.isRate { + return fmt.Sprintf("%s/%d", sel.SelectItems[0].String(), r.opts.RateInterval), visitor.chArgs, nil + } + return sel.SelectItems[0].String(), visitor.chArgs, nil +} + +// RewriteMultiple rewrites a slice of expressions. +func (r *aggExprRewriter) RewriteMultiple( + exprs []string, +) ([]string, [][]any, error) { + out := make([]string, len(exprs)) + var errs []error + var chArgsList [][]any + for i, e := range exprs { + w, chArgs, err := r.Rewrite(e) + if err != nil { + errs = append(errs, err) + out[i] = e + } else { + out[i] = w + chArgsList = append(chArgsList, chArgs) + } + } + if len(errs) > 0 { + return out, nil, errors.Join(errs...) + } + return out, chArgsList, nil +} + +// exprVisitor walks FunctionExpr nodes and applies the mappers. +type exprVisitor struct { + chparser.DefaultASTVisitor + fieldKeys map[string][]*telemetrytypes.TelemetryFieldKey + fullTextColumn *telemetrytypes.TelemetryFieldKey + fieldMapper qbtypes.FieldMapper + conditionBuilder qbtypes.ConditionBuilder + jsonBodyPrefix string + jsonKeyToKey qbtypes.JsonKeyToFieldFunc + Modified bool + chArgs []any + isRate bool +} + +func newExprVisitor( + fieldKeys map[string][]*telemetrytypes.TelemetryFieldKey, + fullTextColumn *telemetrytypes.TelemetryFieldKey, + fieldMapper qbtypes.FieldMapper, + conditionBuilder qbtypes.ConditionBuilder, + jsonBodyPrefix string, + jsonKeyToKey qbtypes.JsonKeyToFieldFunc, +) *exprVisitor { + return &exprVisitor{ + fieldKeys: fieldKeys, + fullTextColumn: fullTextColumn, + fieldMapper: fieldMapper, + conditionBuilder: conditionBuilder, + jsonBodyPrefix: jsonBodyPrefix, + jsonKeyToKey: jsonKeyToKey, + } +} + +// VisitFunctionExpr is invoked for each function call in the AST. +func (v *exprVisitor) VisitFunctionExpr(fn *chparser.FunctionExpr) error { + name := strings.ToLower(fn.Name.Name) + + aggFunc, ok := AggreFuncMap[valuer.NewString(name)] + if !ok { + return nil + } + + var args []chparser.Expr + if fn.Params != nil && fn.Params.Items != nil { + args = fn.Params.Items.Items + } + + // if we know aggregation function, we must ensure that the number of arguments is correct + if aggFunc.RequireArgs { + if len(args) < aggFunc.MinArgs || len(args) > aggFunc.MaxArgs { + return errors.NewInternalf(errors.CodeInternal, "invalid number of arguments for %q: %d", name, len(args)) + } + } + fn.Name.Name = aggFunc.FuncName + if aggFunc.Rate { + v.isRate = true + } + + // Handle *If functions with predicate + values + if aggFunc.FuncCombinator { + // Map the predicate (last argument) + origPred := args[len(args)-1].String() + whereClause, _, err := PrepareWhereClause( + origPred, + FilterExprVisitorOpts{ + FieldKeys: v.fieldKeys, + FieldMapper: v.fieldMapper, + ConditionBuilder: v.conditionBuilder, + FullTextColumn: v.fullTextColumn, + JsonBodyPrefix: v.jsonBodyPrefix, + JsonKeyToKey: v.jsonKeyToKey, + }, + ) + if err != nil { + return err + } + + newPred, chArgs := whereClause.BuildWithFlavor(sqlbuilder.ClickHouse) + newPred = strings.TrimPrefix(newPred, "WHERE") + parsedPred, err := parseFragment(newPred) + if err != nil { + return err + } + args[len(args)-1] = parsedPred + v.Modified = true + v.chArgs = chArgs + + // Map each value column argument + for i := 0; i < len(args)-1; i++ { + origVal := args[i].String() + colName, err := v.fieldMapper.ColumnExpressionFor(context.Background(), &telemetrytypes.TelemetryFieldKey{Name: origVal}, v.fieldKeys) + if err != nil { + return errors.WrapInvalidInputf(err, errors.CodeInvalidInput, "failed to get table field name for %q", origVal) + } + newVal := colName + parsedVal, err := parseFragment(newVal) + if err != nil { + return err + } + args[i] = parsedVal + v.Modified = true + } + } else { + // Non-If functions: map every argument as a column/value + for i, arg := range args { + orig := arg.String() + colName, err := v.fieldMapper.ColumnExpressionFor(context.Background(), &telemetrytypes.TelemetryFieldKey{Name: orig}, v.fieldKeys) + if err != nil { + return errors.WrapInvalidInputf(err, errors.CodeInvalidInput, "failed to get table field name for %q", orig) + } + newCol := colName + parsed, err := parseFragment(newCol) + if err != nil { + return err + } + args[i] = parsed + v.Modified = true + } + if aggFunc.Rate { + v.Modified = true + } + } + + return nil +} + +// parseFragment parses a SQL expression fragment by wrapping in SELECT. +func parseFragment(sql string) (chparser.Expr, error) { + wrapped := fmt.Sprintf("SELECT %s", sql) + p := chparser.NewParser(wrapped) + stmts, err := p.ParseStmts() + if err != nil { + return nil, errors.WrapInternalf(err, errors.CodeInternal, "failed to parse re-written expression %q", sql) + } + sel, ok := stmts[0].(*chparser.SelectQuery) + if !ok { + return nil, errors.NewInternalf(errors.CodeInternal, "unexpected statement type in re-written expression %q: %T", sql, stmts[0]) + } + if len(sel.SelectItems) == 0 { + return nil, errors.NewInternalf(errors.CodeInternal, "no select items in re-written expression %q", sql) + } + return sel.SelectItems[0].Expr, nil +} diff --git a/pkg/querybuilder/filter_error_listener.go b/pkg/querybuilder/filter_error_listener.go new file mode 100644 index 0000000000..ffa623a5f0 --- /dev/null +++ b/pkg/querybuilder/filter_error_listener.go @@ -0,0 +1,235 @@ +package querybuilder + +import ( + "fmt" + "slices" + "sort" + "strings" + + "github.com/antlr4-go/antlr/v4" +) + +var skipTokens = []string{"WS", "COMMENT"} + +// friendly maps SYMBOLIC_NAME -> what the user should see. +var friendly = map[string]string{ + // punctuation & operators + "LPAREN": "(", "RPAREN": ")", + "LBRACK": "[", "RBRACK": "]", + "COMMA": ",", + "EQUALS": "=", + "NOT_EQUALS": "!=", + "NEQ": "<>", + "LT": "<", "LE": "<=", + "GT": ">", "GE": ">=", + + // keywords / functions + "AND": "AND", + "OR": "OR", + "NOT": "NOT", + "LIKE": "LIKE", "ILIKE": "ILIKE", + "NOT_LIKE": "NOT LIKE", "NOT_ILIKE": "NOT ILIKE", + "BETWEEN": "BETWEEN", "IN": "IN", "EXISTS": "EXISTS", + "REGEXP": "REGEXP", "CONTAINS": "CONTAINS", + "HAS": "has()", "HASANY": "hasAny()", "HASALL": "hasAll()", + + // literals / identifiers + "NUMBER": "number", + "BOOL": "boolean", + "QUOTED_TEXT": "quoted text", + "KEY": "field name (ex: service.name)", +} + +// prettyToken returns the nicest human label for token type tType. +// +// Order of preference: +// 1. hard-coded friendly table +// 2. literal name from grammar e.g. "'('" +// 3. symbolic name e.g. AND +// 4. numeric fallback e.g. <34> +func prettyToken(p antlr.Parser, tType int) (string, bool) { + if slices.Contains(skipTokens, tokenName(p, tType)) { + return "", false + } + + // symbolic name -> friendly ? + syms := p.GetSymbolicNames() + if tType >= 0 && tType < len(syms) { + if nice, ok := friendly[syms[tType]]; ok { + return nice, true + } + } + + // literal name (the quoted punctuation that ANTLR generates) + lits := p.GetLiteralNames() + if tType >= 0 && tType < len(lits) && lits[tType] != "" { + return lits[tType], true + } + + // symbolic name as last resort (but hide WS, EOF, …) + if tType >= 0 && tType < len(syms) && syms[tType] != "" && syms[tType] != "WS" { + return syms[tType], true + } + + return "", false // tell caller to skip this entry +} + +type SyntaxErr struct { + Line, Col int + TokenTxt string // offending text (or EOF) + TokenType int // offending token type + Expected []string // token names the parser still expected + RuleStack []string + Msg string +} + +func (e *SyntaxErr) Error() string { + exp := "" + if len(e.Expected) > 0 { + exp = "expecting one of {" + strings.Join(e.Expected, ", ") + "}" + " but got " + e.TokenTxt + } + return fmt.Sprintf("line %d:%d %s", e.Line, e.Col, exp) +} + +type Ambiguity struct { + Text string // slice of raw input that was ambiguous + Alts string // e.g. "{1, 3}" + RStack []string +} + +func (a *Ambiguity) Error() string { + return fmt.Sprintf("ambiguity: %s, alts: %s", a.Text, a.Alts) +} + +type ErrorListener struct { + antlr.DefaultErrorListener + + SyntaxErrors []*SyntaxErr + Ambigs []*Ambiguity +} + +func NewErrorListener() *ErrorListener { return &ErrorListener{} } + +func (l *ErrorListener) SyntaxError( + rec antlr.Recognizer, + off any, + line, column int, + msg string, + e antlr.RecognitionException, +) { + err := &SyntaxErr{Line: line, Col: column, Msg: msg} + + if tok, ok := off.(antlr.Token); ok { + if tok.GetTokenType() == antlr.TokenEOF { + err.TokenTxt = "EOF" + err.TokenType = tok.GetTokenType() + } else { + err.TokenTxt = fmt.Sprintf("'%s'", tok.GetText()) + err.TokenType = tok.GetTokenType() + } + } + + if p, ok := rec.(antlr.Parser); ok { + set := p.GetExpectedTokens() + + // Heuristic: if KEY appears in the expected set *alongside* any literal + // value tokens, we assume it stands for a bare value. Otherwise, it stands + // for a left‑hand identifier. + valueTokens := map[int]struct{}{ + pGetTokenType(p, "QUOTED_TEXT"): {}, + pGetTokenType(p, "NUMBER"): {}, + pGetTokenType(p, "BOOL"): {}, + } + hasValueLiterals := false + for _, iv := range set.GetIntervals() { + for t := iv.Start; t <= iv.Stop; t++ { + if _, ok := valueTokens[t]; ok { + hasValueLiterals = true + break + } + } + if hasValueLiterals { + break + } + } + + uniq := map[string]struct{}{} + for _, iv := range set.GetIntervals() { + for t := iv.Start; t <= iv.Stop; t++ { + sym := tokenName(p, t) + if sym == "KEY" { + if !hasValueLiterals { + uniq["field name (ex: service.name)"] = struct{}{} + } + continue + } + if label, ok := prettyToken(p, t); ok { + uniq[label] = struct{}{} + } + } + } + + err.Expected = make([]string, 0, len(uniq)) + for k := range uniq { + err.Expected = append(err.Expected, k) + } + sort.Strings(err.Expected) + err.RuleStack = p.GetRuleInvocationStack(nil) + } + + l.SyntaxErrors = append(l.SyntaxErrors, err) +} + +func (l *ErrorListener) ReportAmbiguity( + rec antlr.Parser, + dfa *antlr.DFA, + startIdx, stopIdx int, + exact bool, + ambigAlts *antlr.BitSet, + configs *antlr.ATNConfigSet, +) { + if !exact { + return + } + stream := rec.GetTokenStream() + txt := textSlice(stream, startIdx, stopIdx) + l.Ambigs = append(l.Ambigs, &Ambiguity{ + Text: txt, + Alts: ambigAlts.String(), + RStack: rec.GetRuleInvocationStack(nil), + }) +} + +func pGetTokenType(p antlr.Parser, tName string) int { + syms := p.GetSymbolicNames() + for i, sym := range syms { + if sym == tName { + return i + } + } + return -1 +} + +// tokenName prefers literal > symbolic > numeric. +func tokenName(p antlr.Parser, tType int) string { + lits := p.GetLiteralNames() + if tType >= 0 && tType < len(lits) && lits[tType] != "" { + return lits[tType] + } + syms := p.GetSymbolicNames() + if tType >= 0 && tType < len(syms) && syms[tType] != "" { + return syms[tType] + } + return fmt.Sprintf("<%d>", tType) +} + +// textSlice pulls raw input text between two token indexes. +func textSlice(ts antlr.TokenStream, start, stop int) string { + var b strings.Builder + for i := start; i <= stop && i >= 0; i++ { + if tok := ts.Get(i); tok != nil && tok.GetTokenType() != antlr.TokenEOF { + b.WriteString(tok.GetText()) + } + } + return b.String() +} diff --git a/pkg/querybuilder/where_clause_visitor.go b/pkg/querybuilder/where_clause_visitor.go index 2c8ba191d0..4e10dc605a 100644 --- a/pkg/querybuilder/where_clause_visitor.go +++ b/pkg/querybuilder/where_clause_visitor.go @@ -8,7 +8,6 @@ import ( "github.com/SigNoz/signoz/pkg/errors" grammar "github.com/SigNoz/signoz/pkg/parser/grammar" - "github.com/SigNoz/signoz/pkg/telemetrylogs" qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5" "github.com/SigNoz/signoz/pkg/types/telemetrytypes" "github.com/antlr4-go/antlr/v4" @@ -16,75 +15,58 @@ import ( sqlbuilder "github.com/huandu/go-sqlbuilder" ) -// WhereClauseVisitor implements the FilterQueryVisitor interface +// filterExpressionVisitor implements the FilterQueryVisitor interface // to convert the parsed filter expressions into ClickHouse WHERE clause -type WhereClauseVisitor struct { - fieldMapper qbtypes.FieldMapper - conditionBuilder qbtypes.ConditionBuilder - warnings []error - fieldKeys map[string][]*telemetrytypes.TelemetryFieldKey - errors []error - builder *sqlbuilder.SelectBuilder - fullTextColumn *telemetrytypes.TelemetryFieldKey +type filterExpressionVisitor struct { + fieldMapper qbtypes.FieldMapper + conditionBuilder qbtypes.ConditionBuilder + warnings []string + fieldKeys map[string][]*telemetrytypes.TelemetryFieldKey + errors []error + builder *sqlbuilder.SelectBuilder + fullTextColumn *telemetrytypes.TelemetryFieldKey + jsonBodyPrefix string + jsonKeyToKey qbtypes.JsonKeyToFieldFunc + skipResourceFilter bool } -// NewWhereClauseVisitor creates a new WhereClauseVisitor -func NewWhereClauseVisitor( - conditionBuilder qbtypes.ConditionBuilder, - fieldKeys map[string][]*telemetrytypes.TelemetryFieldKey, - builder *sqlbuilder.SelectBuilder, - fullTextColumn *telemetrytypes.TelemetryFieldKey, -) *WhereClauseVisitor { - return &WhereClauseVisitor{ - conditionBuilder: conditionBuilder, - fieldKeys: fieldKeys, - builder: builder, - fullTextColumn: fullTextColumn, +type FilterExprVisitorOpts struct { + FieldMapper qbtypes.FieldMapper + ConditionBuilder qbtypes.ConditionBuilder + FieldKeys map[string][]*telemetrytypes.TelemetryFieldKey + Builder *sqlbuilder.SelectBuilder + FullTextColumn *telemetrytypes.TelemetryFieldKey + JsonBodyPrefix string + JsonKeyToKey qbtypes.JsonKeyToFieldFunc + SkipResourceFilter bool +} + +// newFilterExpressionVisitor creates a new filterExpressionVisitor +func newFilterExpressionVisitor(opts FilterExprVisitorOpts) *filterExpressionVisitor { + return &filterExpressionVisitor{ + fieldMapper: opts.FieldMapper, + conditionBuilder: opts.ConditionBuilder, + fieldKeys: opts.FieldKeys, + builder: opts.Builder, + fullTextColumn: opts.FullTextColumn, + jsonBodyPrefix: opts.JsonBodyPrefix, + jsonKeyToKey: opts.JsonKeyToKey, + skipResourceFilter: opts.SkipResourceFilter, } } -type SyntaxError struct { - line, column int - msg string -} - -func (e *SyntaxError) Error() string { - return fmt.Sprintf("line %d:%d %s", e.line, e.column, e.msg) -} - -// ErrorListener is a custom error listener to capture syntax errors -type ErrorListener struct { - *antlr.DefaultErrorListener - Errors []error -} - -// NewErrorListener creates a new error listener -func NewErrorListener() *ErrorListener { - return &ErrorListener{ - DefaultErrorListener: antlr.NewDefaultErrorListener(), - Errors: []error{}, - } -} - -// SyntaxError captures syntax errors during parsing -func (l *ErrorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol any, line, column int, msg string, e antlr.RecognitionException) { - l.Errors = append(l.Errors, &SyntaxError{line: line, column: column, msg: msg}) -} - // PrepareWhereClause generates a ClickHouse compatible WHERE clause from the filter query -func PrepareWhereClause( - query string, - fieldKeys map[string][]*telemetrytypes.TelemetryFieldKey, - conditionBuilder qbtypes.ConditionBuilder, - fullTextColumn *telemetrytypes.TelemetryFieldKey, -) (*sqlbuilder.WhereClause, []error, error) { +func PrepareWhereClause(query string, opts FilterExprVisitorOpts) (*sqlbuilder.WhereClause, []string, error) { // Setup the ANTLR parsing pipeline input := antlr.NewInputStream(query) lexer := grammar.NewFilterQueryLexer(input) - sb := sqlbuilder.NewSelectBuilder() + if opts.Builder == nil { + sb := sqlbuilder.NewSelectBuilder() + opts.Builder = sb + } - visitor := NewWhereClauseVisitor(conditionBuilder, fieldKeys, sb, fullTextColumn) + visitor := newFilterExpressionVisitor(opts) // Set up error handling lexerErrorListener := NewErrorListener() @@ -101,13 +83,13 @@ func PrepareWhereClause( tree := parser.Query() // Handle syntax errors - if len(parserErrorListener.Errors) > 0 { + if len(parserErrorListener.SyntaxErrors) > 0 { combinedErrors := errors.Newf( errors.TypeInvalidInput, errors.CodeInvalidInput, - "found %d syntax errors while parsing the search expression: %v", - len(parserErrorListener.Errors), - parserErrorListener.Errors, + "found %d syntax errors while parsing the filter expression: %v", + len(parserErrorListener.SyntaxErrors), + parserErrorListener.SyntaxErrors, ) return nil, nil, combinedErrors } @@ -133,7 +115,7 @@ func PrepareWhereClause( } // Visit dispatches to the specific visit method based on node type -func (v *WhereClauseVisitor) Visit(tree antlr.ParseTree) any { +func (v *filterExpressionVisitor) Visit(tree antlr.ParseTree) any { // Handle nil nodes to prevent panic if tree == nil { return "" @@ -179,18 +161,18 @@ func (v *WhereClauseVisitor) Visit(tree antlr.ParseTree) any { } } -func (v *WhereClauseVisitor) VisitQuery(ctx *grammar.QueryContext) any { +func (v *filterExpressionVisitor) VisitQuery(ctx *grammar.QueryContext) any { return v.Visit(ctx.Expression()) } // VisitExpression passes through to the orExpression -func (v *WhereClauseVisitor) VisitExpression(ctx *grammar.ExpressionContext) any { +func (v *filterExpressionVisitor) VisitExpression(ctx *grammar.ExpressionContext) any { return v.Visit(ctx.OrExpression()) } // VisitOrExpression handles OR expressions -func (v *WhereClauseVisitor) VisitOrExpression(ctx *grammar.OrExpressionContext) any { +func (v *filterExpressionVisitor) VisitOrExpression(ctx *grammar.OrExpressionContext) any { andExpressions := ctx.AllAndExpression() andExpressionConditions := make([]string, len(andExpressions)) @@ -206,7 +188,7 @@ func (v *WhereClauseVisitor) VisitOrExpression(ctx *grammar.OrExpressionContext) } // VisitAndExpression handles AND expressions -func (v *WhereClauseVisitor) VisitAndExpression(ctx *grammar.AndExpressionContext) any { +func (v *filterExpressionVisitor) VisitAndExpression(ctx *grammar.AndExpressionContext) any { unaryExpressions := ctx.AllUnaryExpression() unaryExpressionConditions := make([]string, len(unaryExpressions)) @@ -222,7 +204,7 @@ func (v *WhereClauseVisitor) VisitAndExpression(ctx *grammar.AndExpressionContex } // VisitUnaryExpression handles NOT expressions -func (v *WhereClauseVisitor) VisitUnaryExpression(ctx *grammar.UnaryExpressionContext) any { +func (v *filterExpressionVisitor) VisitUnaryExpression(ctx *grammar.UnaryExpressionContext) any { result := v.Visit(ctx.Primary()).(string) // Check if this is a NOT expression @@ -234,7 +216,7 @@ func (v *WhereClauseVisitor) VisitUnaryExpression(ctx *grammar.UnaryExpressionCo } // VisitPrimary handles grouped expressions, comparisons, function calls, and full-text search -func (v *WhereClauseVisitor) VisitPrimary(ctx *grammar.PrimaryContext) any { +func (v *filterExpressionVisitor) VisitPrimary(ctx *grammar.PrimaryContext) any { if ctx.OrExpression() != nil { // This is a parenthesized expression return fmt.Sprintf("(%s)", v.Visit(ctx.OrExpression()).(string)) @@ -246,14 +228,43 @@ func (v *WhereClauseVisitor) VisitPrimary(ctx *grammar.PrimaryContext) any { return v.Visit(ctx.FullText()) } - // Handle standalone key as a full text search term + // Handle standalone key/value as a full text search term if ctx.GetChildCount() == 1 { + if v.fullTextColumn == nil { + v.errors = append(v.errors, errors.Newf( + errors.TypeInvalidInput, + errors.CodeInvalidInput, + "full text search is not supported", + )) + return "" + } child := ctx.GetChild(0) if keyCtx, ok := child.(*grammar.KeyContext); ok { // create a full text search condition on the body field keyText := keyCtx.GetText() cond, err := v.conditionBuilder.ConditionFor(context.Background(), v.fullTextColumn, qbtypes.FilterOperatorRegexp, keyText, v.builder) if err != nil { + v.errors = append(v.errors, errors.WrapInternalf(err, errors.CodeInternal, "failed to build full text search condition")) + return "" + } + return cond + } else if valCtx, ok := child.(*grammar.ValueContext); ok { + var text string + if valCtx.QUOTED_TEXT() != nil { + text = trimQuotes(valCtx.QUOTED_TEXT().GetText()) + } else if valCtx.NUMBER() != nil { + text = valCtx.NUMBER().GetText() + } else if valCtx.BOOL() != nil { + text = valCtx.BOOL().GetText() + } else if valCtx.KEY() != nil { + text = valCtx.KEY().GetText() + } else { + v.errors = append(v.errors, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "unsupported value type: %s", valCtx.GetText())) + return "" + } + cond, err := v.conditionBuilder.ConditionFor(context.Background(), v.fullTextColumn, qbtypes.FilterOperatorRegexp, text, v.builder) + if err != nil { + v.errors = append(v.errors, errors.WrapInternalf(err, errors.CodeInternal, "failed to build full text search condition")) return "" } return cond @@ -264,9 +275,21 @@ func (v *WhereClauseVisitor) VisitPrimary(ctx *grammar.PrimaryContext) any { } // VisitComparison handles all comparison operators -func (v *WhereClauseVisitor) VisitComparison(ctx *grammar.ComparisonContext) any { +func (v *filterExpressionVisitor) VisitComparison(ctx *grammar.ComparisonContext) any { keys := v.Visit(ctx.Key()).([]*telemetrytypes.TelemetryFieldKey) + // this is used to skip the resource filtering on main table if + // the query may use the resources table sub-query filter + if v.skipResourceFilter { + filteredKeys := []*telemetrytypes.TelemetryFieldKey{} + for _, key := range keys { + if key.FieldContext != telemetrytypes.FieldContextResource { + filteredKeys = append(filteredKeys, key) + } + } + keys = filteredKeys + } + // Handle EXISTS specially if ctx.EXISTS() != nil { op := qbtypes.FilterOperatorExists @@ -281,12 +304,23 @@ func (v *WhereClauseVisitor) VisitComparison(ctx *grammar.ComparisonContext) any } conds = append(conds, condition) } + // if there is only one condition, return it directly, one less `()` wrapper + if len(conds) == 1 { + return conds[0] + } return v.builder.Or(conds...) } // Handle IN clause if ctx.InClause() != nil || ctx.NotInClause() != nil { - values := v.Visit(ctx.InClause()).([]any) + + var values []any + if ctx.InClause() != nil { + values = v.Visit(ctx.InClause()).([]any) + } else if ctx.NotInClause() != nil { + values = v.Visit(ctx.NotInClause()).([]any) + } + op := qbtypes.FilterOperatorIn if ctx.NotInClause() != nil { op = qbtypes.FilterOperatorNotIn @@ -299,6 +333,9 @@ func (v *WhereClauseVisitor) VisitComparison(ctx *grammar.ComparisonContext) any } conds = append(conds, condition) } + if len(conds) == 1 { + return conds[0] + } return v.builder.Or(conds...) } @@ -325,6 +362,9 @@ func (v *WhereClauseVisitor) VisitComparison(ctx *grammar.ComparisonContext) any } conds = append(conds, condition) } + if len(conds) == 1 { + return conds[0] + } return v.builder.Or(conds...) } @@ -351,29 +391,35 @@ func (v *WhereClauseVisitor) VisitComparison(ctx *grammar.ComparisonContext) any } else if ctx.LIKE() != nil { op = qbtypes.FilterOperatorLike } else if ctx.ILIKE() != nil { - op = qbtypes.FilterOperatorLike + op = qbtypes.FilterOperatorILike } else if ctx.NOT_LIKE() != nil { op = qbtypes.FilterOperatorNotLike } else if ctx.NOT_ILIKE() != nil { - op = qbtypes.FilterOperatorNotLike + op = qbtypes.FilterOperatorNotILike } else if ctx.REGEXP() != nil { op = qbtypes.FilterOperatorRegexp - } else if ctx.NOT() != nil && ctx.REGEXP() != nil { - op = qbtypes.FilterOperatorNotRegexp + if ctx.NOT() != nil { + op = qbtypes.FilterOperatorNotRegexp + } } else if ctx.CONTAINS() != nil { op = qbtypes.FilterOperatorContains - } else if ctx.NOT() != nil && ctx.CONTAINS() != nil { - op = qbtypes.FilterOperatorNotContains + if ctx.NOT() != nil { + op = qbtypes.FilterOperatorNotContains + } } var conds []string for _, key := range keys { condition, err := v.conditionBuilder.ConditionFor(context.Background(), key, op, value, v.builder) if err != nil { + v.errors = append(v.errors, errors.WrapInternalf(err, errors.CodeInternal, "failed to build condition")) return "" } conds = append(conds, condition) } + if len(conds) == 1 { + return conds[0] + } return v.builder.Or(conds...) } @@ -381,17 +427,17 @@ func (v *WhereClauseVisitor) VisitComparison(ctx *grammar.ComparisonContext) any } // VisitInClause handles IN expressions -func (v *WhereClauseVisitor) VisitInClause(ctx *grammar.InClauseContext) any { +func (v *filterExpressionVisitor) VisitInClause(ctx *grammar.InClauseContext) any { return v.Visit(ctx.ValueList()) } // VisitNotInClause handles NOT IN expressions -func (v *WhereClauseVisitor) VisitNotInClause(ctx *grammar.NotInClauseContext) any { +func (v *filterExpressionVisitor) VisitNotInClause(ctx *grammar.NotInClauseContext) any { return v.Visit(ctx.ValueList()) } // VisitValueList handles comma-separated value lists -func (v *WhereClauseVisitor) VisitValueList(ctx *grammar.ValueListContext) any { +func (v *filterExpressionVisitor) VisitValueList(ctx *grammar.ValueListContext) any { values := ctx.AllValue() parts := []any{} @@ -403,18 +449,34 @@ func (v *WhereClauseVisitor) VisitValueList(ctx *grammar.ValueListContext) any { } // VisitFullText handles standalone quoted strings for full-text search -func (v *WhereClauseVisitor) VisitFullText(ctx *grammar.FullTextContext) any { - // remove quotes from the quotedText - quotedText := strings.Trim(ctx.QUOTED_TEXT().GetText(), "\"'") - cond, err := v.conditionBuilder.ConditionFor(context.Background(), v.fullTextColumn, qbtypes.FilterOperatorRegexp, quotedText, v.builder) +func (v *filterExpressionVisitor) VisitFullText(ctx *grammar.FullTextContext) any { + + var text string + + if ctx.QUOTED_TEXT() != nil { + text = trimQuotes(ctx.QUOTED_TEXT().GetText()) + } else if ctx.FREETEXT() != nil { + text = ctx.FREETEXT().GetText() + } + + if v.fullTextColumn == nil { + v.errors = append(v.errors, errors.Newf( + errors.TypeInvalidInput, + errors.CodeInvalidInput, + "full text search is not supported", + )) + return "" + } + cond, err := v.conditionBuilder.ConditionFor(context.Background(), v.fullTextColumn, qbtypes.FilterOperatorRegexp, text, v.builder) if err != nil { + v.errors = append(v.errors, errors.WrapInternalf(err, errors.CodeInternal, "failed to build full text search condition")) return "" } return cond } // VisitFunctionCall handles function calls like has(), hasAny(), etc. -func (v *WhereClauseVisitor) VisitFunctionCall(ctx *grammar.FunctionCallContext) any { +func (v *filterExpressionVisitor) VisitFunctionCall(ctx *grammar.FunctionCallContext) any { // Get function name based on which token is present var functionName string if ctx.HAS() != nil { @@ -460,10 +522,16 @@ func (v *WhereClauseVisitor) VisitFunctionCall(ctx *grammar.FunctionCallContext) for _, key := range keys { var fieldName string - if strings.HasPrefix(key.Name, telemetrylogs.BodyJSONStringSearchPrefix) { - fieldName, _ = telemetrylogs.GetBodyJSONKey(context.Background(), key, qbtypes.FilterOperatorUnknown, value) + if strings.HasPrefix(key.Name, v.jsonBodyPrefix) { + fieldName, _ = v.jsonKeyToKey(context.Background(), key, qbtypes.FilterOperatorUnknown, value) } else { - fieldName, _ = v.fieldMapper.FieldFor(context.Background(), key) + v.errors = append(v.errors, errors.Newf( + errors.TypeInvalidInput, + errors.CodeInvalidInput, + "function `%s` supports only body JSON search", + functionName, + )) + return "" } var cond string @@ -479,11 +547,14 @@ func (v *WhereClauseVisitor) VisitFunctionCall(ctx *grammar.FunctionCallContext) conds = append(conds, cond) } + if len(conds) == 1 { + return conds[0] + } return v.builder.Or(conds...) } // VisitFunctionParamList handles the parameter list for function calls -func (v *WhereClauseVisitor) VisitFunctionParamList(ctx *grammar.FunctionParamListContext) any { +func (v *filterExpressionVisitor) VisitFunctionParamList(ctx *grammar.FunctionParamListContext) any { params := ctx.AllFunctionParam() parts := make([]any, len(params)) @@ -495,7 +566,7 @@ func (v *WhereClauseVisitor) VisitFunctionParamList(ctx *grammar.FunctionParamLi } // VisitFunctionParam handles individual parameters in function calls -func (v *WhereClauseVisitor) VisitFunctionParam(ctx *grammar.FunctionParamContext) any { +func (v *filterExpressionVisitor) VisitFunctionParam(ctx *grammar.FunctionParamContext) any { if ctx.Key() != nil { return v.Visit(ctx.Key()) } else if ctx.Value() != nil { @@ -508,16 +579,16 @@ func (v *WhereClauseVisitor) VisitFunctionParam(ctx *grammar.FunctionParamContex } // VisitArray handles array literals -func (v *WhereClauseVisitor) VisitArray(ctx *grammar.ArrayContext) any { +func (v *filterExpressionVisitor) VisitArray(ctx *grammar.ArrayContext) any { return v.Visit(ctx.ValueList()) } // VisitValue handles literal values: strings, numbers, booleans -func (v *WhereClauseVisitor) VisitValue(ctx *grammar.ValueContext) any { +func (v *filterExpressionVisitor) VisitValue(ctx *grammar.ValueContext) any { if ctx.QUOTED_TEXT() != nil { txt := ctx.QUOTED_TEXT().GetText() // trim quotes and return the value - return strings.Trim(txt, "\"'") + return trimQuotes(txt) } else if ctx.NUMBER() != nil { number, err := strconv.ParseFloat(ctx.NUMBER().GetText(), 64) if err != nil { @@ -546,11 +617,11 @@ func (v *WhereClauseVisitor) VisitValue(ctx *grammar.ValueContext) any { } // VisitKey handles field/column references -func (v *WhereClauseVisitor) VisitKey(ctx *grammar.KeyContext) any { +func (v *filterExpressionVisitor) VisitKey(ctx *grammar.KeyContext) any { - fieldKey := telemetrytypes.GetFieldKeyFromKeyText(ctx.KEY().GetText()) + fieldKey := telemetrytypes.GetFieldKeyFromKeyText(ctx.GetText()) - keyName := strings.TrimPrefix(fieldKey.Name, telemetrylogs.BodyJSONStringSearchPrefix) + keyName := strings.TrimPrefix(fieldKey.Name, v.jsonBodyPrefix) fieldKeysForName := v.fieldKeys[keyName] @@ -558,26 +629,33 @@ func (v *WhereClauseVisitor) VisitKey(ctx *grammar.KeyContext) any { // if there is a field with the same name as attribute/resource attribute // Since it will ORed with the fieldKeysForName, it will not result empty // when either of them have values - if strings.HasPrefix(fieldKey.Name, telemetrylogs.BodyJSONStringSearchPrefix) { - fieldKeysForName = append(fieldKeysForName, &fieldKey) + if strings.HasPrefix(fieldKey.Name, v.jsonBodyPrefix) && v.jsonBodyPrefix != "" { + if keyName != "" { + fieldKeysForName = append(fieldKeysForName, &fieldKey) + } } - // TODO(srikanthccv): do we want to return an error here? - // should we infer the type and auto-magically build a key for expression? if len(fieldKeysForName) == 0 { - v.errors = append(v.errors, errors.Newf( - errors.TypeInvalidInput, - errors.CodeInvalidInput, - "key `%s` not found", - fieldKey.Name, - )) + if strings.HasPrefix(fieldKey.Name, v.jsonBodyPrefix) && v.jsonBodyPrefix != "" && keyName == "" { + v.errors = append(v.errors, errors.NewInvalidInputf( + errors.CodeInvalidInput, + "missing key for body json search - expected key of the form `body.key` (ex: `body.status`)", + )) + } else { + // TODO(srikanthccv): do we want to return an error here? + // should we infer the type and auto-magically build a key for expression? + v.errors = append(v.errors, errors.Newf( + errors.TypeInvalidInput, + errors.CodeInvalidInput, + "key `%s` not found", + fieldKey.Name, + )) + } } if len(fieldKeysForName) > 1 { // this is warning state, we must have a unambiguous key - v.warnings = append(v.warnings, errors.Newf( - errors.TypeInvalidInput, - errors.CodeInvalidInput, + v.warnings = append(v.warnings, fmt.Sprintf( "key `%s` is ambiguous, found %d different combinations of field context and data type: %v", fieldKey.Name, len(fieldKeysForName), @@ -587,3 +665,13 @@ func (v *WhereClauseVisitor) VisitKey(ctx *grammar.KeyContext) any { return fieldKeysForName } + +func trimQuotes(txt string) string { + if len(txt) >= 2 { + if (txt[0] == '"' && txt[len(txt)-1] == '"') || + (txt[0] == '\'' && txt[len(txt)-1] == '\'') { + return txt[1 : len(txt)-1] + } + } + return txt +} diff --git a/pkg/telemetrylogs/condition_builder.go b/pkg/telemetrylogs/condition_builder.go index 4e432a5ec2..da171ca51f 100644 --- a/pkg/telemetrylogs/condition_builder.go +++ b/pkg/telemetrylogs/condition_builder.go @@ -3,6 +3,7 @@ package telemetrylogs import ( "context" "fmt" + "slices" "strings" schema "github.com/SigNoz/signoz-otel-collector/cmd/signozschemamigrator/schema_migrator" @@ -20,7 +21,7 @@ func NewConditionBuilder(fm qbtypes.FieldMapper) *conditionBuilder { return &conditionBuilder{fm: fm} } -func (c *conditionBuilder) ConditionFor( +func (c *conditionBuilder) conditionFor( ctx context.Context, key *telemetrytypes.TelemetryFieldKey, operator qbtypes.FilterOperator, @@ -44,6 +45,16 @@ func (c *conditionBuilder) ConditionFor( tblFieldName, value = telemetrytypes.DataTypeCollisionHandledFieldName(key, value, tblFieldName) + // make use of case insensitive index for body + if tblFieldName == "body" { + switch operator { + case qbtypes.FilterOperatorLike: + return sb.ILike(tblFieldName, value), nil + case qbtypes.FilterOperatorNotLike: + return sb.NotILike(tblFieldName, value), nil + } + } + // regular operators switch operator { // regular operators @@ -76,11 +87,9 @@ func (c *conditionBuilder) ConditionFor( return sb.NotILike(tblFieldName, fmt.Sprintf("%%%s%%", value)), nil case qbtypes.FilterOperatorRegexp: - exp := fmt.Sprintf(`match(%s, %s)`, tblFieldName, sb.Var(value)) - return sb.And(exp), nil + return fmt.Sprintf(`match(%s, %s)`, tblFieldName, sb.Var(value)), nil case qbtypes.FilterOperatorNotRegexp: - exp := fmt.Sprintf(`not match(%s, %s)`, tblFieldName, sb.Var(value)) - return sb.And(exp), nil + return fmt.Sprintf(`NOT match(%s, %s)`, tblFieldName, sb.Var(value)), nil // between and not between case qbtypes.FilterOperatorBetween: values, ok := value.([]any) @@ -107,18 +116,37 @@ func (c *conditionBuilder) ConditionFor( if !ok { return "", qbtypes.ErrInValues } - return sb.In(tblFieldName, values...), nil + // instead of using IN, we use `=` + `OR` to make use of index + conditions := []string{} + for _, value := range values { + conditions = append(conditions, sb.E(tblFieldName, value)) + } + return sb.Or(conditions...), nil case qbtypes.FilterOperatorNotIn: values, ok := value.([]any) if !ok { return "", qbtypes.ErrInValues } - return sb.NotIn(tblFieldName, values...), nil + // instead of using NOT IN, we use `!=` + `AND` to make use of index + conditions := []string{} + for _, value := range values { + conditions = append(conditions, sb.NE(tblFieldName, value)) + } + return sb.And(conditions...), nil // exists and not exists // in the UI based query builder, `exists` and `not exists` are used for // key membership checks, so depending on the column type, the condition changes case qbtypes.FilterOperatorExists, qbtypes.FilterOperatorNotExists: + + if strings.HasPrefix(key.Name, BodyJSONStringSearchPrefix) { + if operator == qbtypes.FilterOperatorExists { + return GetBodyJSONKeyForExists(ctx, key, operator, value), nil + } else { + return "NOT " + GetBodyJSONKeyForExists(ctx, key, operator, value), nil + } + } + var value any switch column.Type { case schema.ColumnTypeString, schema.LowCardinalityColumnType{ElementType: schema.ColumnTypeString}: @@ -160,3 +188,32 @@ func (c *conditionBuilder) ConditionFor( } return "", fmt.Errorf("unsupported operator: %v", operator) } + +func (c *conditionBuilder) ConditionFor( + ctx context.Context, + key *telemetrytypes.TelemetryFieldKey, + operator qbtypes.FilterOperator, + value any, + sb *sqlbuilder.SelectBuilder, +) (string, error) { + condition, err := c.conditionFor(ctx, key, operator, value, sb) + if err != nil { + return "", err + } + + if operator.AddDefaultExistsFilter() { + // skip adding exists filter for intrinsic fields + // with an exception for body json search + field, _ := c.fm.FieldFor(ctx, key) + if slices.Contains(IntrinsicFields, field) && !strings.HasPrefix(key.Name, BodyJSONStringSearchPrefix) { + return condition, nil + } + + existsCondition, err := c.conditionFor(ctx, key, qbtypes.FilterOperatorExists, nil, sb) + if err != nil { + return "", err + } + return sb.And(condition, existsCondition), nil + } + return condition, nil +} diff --git a/pkg/telemetrylogs/condition_builder_test.go b/pkg/telemetrylogs/condition_builder_test.go index 3adb4f64de..45f61b2d03 100644 --- a/pkg/telemetrylogs/condition_builder_test.go +++ b/pkg/telemetrylogs/condition_builder_test.go @@ -20,6 +20,7 @@ func TestConditionFor(t *testing.T) { operator qbtypes.FilterOperator value any expectedSQL string + expectedArgs []any expectedError error }{ { @@ -31,6 +32,7 @@ func TestConditionFor(t *testing.T) { operator: qbtypes.FilterOperatorEqual, value: "error message", expectedSQL: "body = ?", + expectedArgs: []any{"error message"}, expectedError: nil, }, { @@ -42,6 +44,7 @@ func TestConditionFor(t *testing.T) { operator: qbtypes.FilterOperatorNotEqual, value: uint64(1617979338000000000), expectedSQL: "timestamp <> ?", + expectedArgs: []any{uint64(1617979338000000000)}, expectedError: nil, }, { @@ -53,7 +56,8 @@ func TestConditionFor(t *testing.T) { }, operator: qbtypes.FilterOperatorGreaterThan, value: float64(100), - expectedSQL: "attributes_number['request.duration'] > ?", + expectedSQL: "(attributes_number['request.duration'] > ? AND mapContains(attributes_number, 'request.duration') = ?)", + expectedArgs: []any{float64(100), true}, expectedError: nil, }, { @@ -65,7 +69,8 @@ func TestConditionFor(t *testing.T) { }, operator: qbtypes.FilterOperatorLessThan, value: float64(1024), - expectedSQL: "attributes_number['request.size'] < ?", + expectedSQL: "(attributes_number['request.size'] < ? AND mapContains(attributes_number, 'request.size') = ?)", + expectedArgs: []any{float64(1024), true}, expectedError: nil, }, { @@ -77,6 +82,7 @@ func TestConditionFor(t *testing.T) { operator: qbtypes.FilterOperatorGreaterThanOrEq, value: uint64(1617979338000000000), expectedSQL: "timestamp >= ?", + expectedArgs: []any{uint64(1617979338000000000)}, expectedError: nil, }, { @@ -88,6 +94,7 @@ func TestConditionFor(t *testing.T) { operator: qbtypes.FilterOperatorLessThanOrEq, value: uint64(1617979338000000000), expectedSQL: "timestamp <= ?", + expectedArgs: []any{uint64(1617979338000000000)}, expectedError: nil, }, { @@ -98,7 +105,8 @@ func TestConditionFor(t *testing.T) { }, operator: qbtypes.FilterOperatorLike, value: "%error%", - expectedSQL: "body LIKE ?", + expectedSQL: "LOWER(body) LIKE LOWER(?)", + expectedArgs: []any{"%error%"}, expectedError: nil, }, { @@ -109,7 +117,8 @@ func TestConditionFor(t *testing.T) { }, operator: qbtypes.FilterOperatorNotLike, value: "%error%", - expectedSQL: "body NOT LIKE ?", + expectedSQL: "LOWER(body) NOT LIKE LOWER(?)", + expectedArgs: []any{"%error%"}, expectedError: nil, }, { @@ -121,7 +130,8 @@ func TestConditionFor(t *testing.T) { }, operator: qbtypes.FilterOperatorILike, value: "%admin%", - expectedSQL: "WHERE LOWER(attributes_string['user.id']) LIKE LOWER(?)", + expectedSQL: "(LOWER(attributes_string['user.id']) LIKE LOWER(?) AND mapContains(attributes_string, 'user.id') = ?)", + expectedArgs: []any{"%admin%", true}, expectedError: nil, }, { @@ -134,6 +144,7 @@ func TestConditionFor(t *testing.T) { operator: qbtypes.FilterOperatorNotILike, value: "%admin%", expectedSQL: "WHERE LOWER(attributes_string['user.id']) NOT LIKE LOWER(?)", + expectedArgs: []any{"%admin%"}, expectedError: nil, }, { @@ -145,7 +156,8 @@ func TestConditionFor(t *testing.T) { }, operator: qbtypes.FilterOperatorContains, value: "admin", - expectedSQL: "WHERE LOWER(attributes_string['user.id']) LIKE LOWER(?)", + expectedSQL: "(LOWER(attributes_string['user.id']) LIKE LOWER(?) AND mapContains(attributes_string, 'user.id') = ?)", + expectedArgs: []any{"%admin%", true}, expectedError: nil, }, { @@ -157,6 +169,7 @@ func TestConditionFor(t *testing.T) { operator: qbtypes.FilterOperatorBetween, value: []any{uint64(1617979338000000000), uint64(1617979348000000000)}, expectedSQL: "timestamp BETWEEN ? AND ?", + expectedArgs: []any{uint64(1617979338000000000), uint64(1617979348000000000)}, expectedError: nil, }, { @@ -190,6 +203,7 @@ func TestConditionFor(t *testing.T) { operator: qbtypes.FilterOperatorNotBetween, value: []any{uint64(1617979338000000000), uint64(1617979348000000000)}, expectedSQL: "timestamp NOT BETWEEN ? AND ?", + expectedArgs: []any{uint64(1617979338000000000), uint64(1617979348000000000)}, expectedError: nil, }, { @@ -200,7 +214,8 @@ func TestConditionFor(t *testing.T) { }, operator: qbtypes.FilterOperatorIn, value: []any{"error", "fatal", "critical"}, - expectedSQL: "severity_text IN (?, ?, ?)", + expectedSQL: "(severity_text = ? OR severity_text = ? OR severity_text = ?)", + expectedArgs: []any{"error", "fatal", "critical"}, expectedError: nil, }, { @@ -222,7 +237,8 @@ func TestConditionFor(t *testing.T) { }, operator: qbtypes.FilterOperatorNotIn, value: []any{"debug", "info", "trace"}, - expectedSQL: "severity_text NOT IN (?, ?, ?)", + expectedSQL: "(severity_text <> ? AND severity_text <> ? AND severity_text <> ?)", + expectedArgs: []any{"debug", "info", "trace"}, expectedError: nil, }, { @@ -234,6 +250,7 @@ func TestConditionFor(t *testing.T) { operator: qbtypes.FilterOperatorExists, value: nil, expectedSQL: "body <> ?", + expectedArgs: []any{""}, expectedError: nil, }, { @@ -245,6 +262,7 @@ func TestConditionFor(t *testing.T) { operator: qbtypes.FilterOperatorNotExists, value: nil, expectedSQL: "body = ?", + expectedArgs: []any{""}, expectedError: nil, }, { @@ -256,6 +274,7 @@ func TestConditionFor(t *testing.T) { operator: qbtypes.FilterOperatorExists, value: nil, expectedSQL: "timestamp <> ?", + expectedArgs: []any{0}, expectedError: nil, }, { @@ -268,6 +287,7 @@ func TestConditionFor(t *testing.T) { operator: qbtypes.FilterOperatorExists, value: nil, expectedSQL: "mapContains(attributes_string, 'user.id') = ?", + expectedArgs: []any{true}, expectedError: nil, }, { @@ -280,6 +300,7 @@ func TestConditionFor(t *testing.T) { operator: qbtypes.FilterOperatorNotExists, value: nil, expectedSQL: "mapContains(attributes_string, 'user.id') <> ?", + expectedArgs: []any{true}, expectedError: nil, }, { @@ -308,8 +329,9 @@ func TestConditionFor(t *testing.T) { assert.Equal(t, tc.expectedError, err) } else { require.NoError(t, err) - sql, _ := sb.BuildWithFlavor(sqlbuilder.ClickHouse) + sql, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse) assert.Contains(t, sql, tc.expectedSQL) + assert.Equal(t, tc.expectedArgs, args) } }) } @@ -324,6 +346,7 @@ func TestConditionForMultipleKeys(t *testing.T) { operator qbtypes.FilterOperator value any expectedSQL string + expectedArgs []any expectedError error }{ { @@ -341,6 +364,7 @@ func TestConditionForMultipleKeys(t *testing.T) { operator: qbtypes.FilterOperatorEqual, value: "error message", expectedSQL: "body = ? AND severity_text = ?", + expectedArgs: []any{"error message", "error message"}, expectedError: nil, }, } @@ -389,7 +413,7 @@ func TestConditionForJSONBodySearch(t *testing.T) { }, operator: qbtypes.FilterOperatorEqual, value: 200, - expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'Int64') = ?", + expectedSQL: `JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') = ?`, expectedError: nil, }, { @@ -399,7 +423,7 @@ func TestConditionForJSONBodySearch(t *testing.T) { }, operator: qbtypes.FilterOperatorEqual, value: 405.5, - expectedSQL: "JSONExtract(JSON_VALUE(body, '$.duration_ms'), 'Float64') = ?", + expectedSQL: `JSONExtract(JSON_VALUE(body, '$."duration_ms"'), 'Float64') = ?`, expectedError: nil, }, { @@ -409,7 +433,7 @@ func TestConditionForJSONBodySearch(t *testing.T) { }, operator: qbtypes.FilterOperatorEqual, value: "GET", - expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.method'), 'String') = ?", + expectedSQL: `JSONExtract(JSON_VALUE(body, '$."http"."method"'), 'String') = ?`, expectedError: nil, }, { @@ -419,7 +443,7 @@ func TestConditionForJSONBodySearch(t *testing.T) { }, operator: qbtypes.FilterOperatorEqual, value: true, - expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.success'), 'Bool') = ?", + expectedSQL: `JSONExtract(JSON_VALUE(body, '$."http"."success"'), 'Bool') = ?`, expectedError: nil, }, { @@ -429,7 +453,7 @@ func TestConditionForJSONBodySearch(t *testing.T) { }, operator: qbtypes.FilterOperatorExists, value: nil, - expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'String') <> ?", + expectedSQL: `JSON_EXISTS(body, '$."http"."status_code"')`, expectedError: nil, }, { @@ -439,7 +463,7 @@ func TestConditionForJSONBodySearch(t *testing.T) { }, operator: qbtypes.FilterOperatorNotExists, value: nil, - expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'String') = ?", + expectedSQL: `NOT JSON_EXISTS(body, '$."http"."status_code"')`, expectedError: nil, }, { @@ -449,7 +473,7 @@ func TestConditionForJSONBodySearch(t *testing.T) { }, operator: qbtypes.FilterOperatorGreaterThan, value: "200", - expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'Int64') > ?", + expectedSQL: `JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') > ?`, expectedError: nil, }, { @@ -459,7 +483,7 @@ func TestConditionForJSONBodySearch(t *testing.T) { }, operator: qbtypes.FilterOperatorGreaterThan, value: 200, - expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'Int64') > ?", + expectedSQL: `JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') > ?`, expectedError: nil, }, { @@ -469,7 +493,7 @@ func TestConditionForJSONBodySearch(t *testing.T) { }, operator: qbtypes.FilterOperatorLessThan, value: "300", - expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'Int64') < ?", + expectedSQL: `JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') < ?`, expectedError: nil, }, { @@ -479,7 +503,7 @@ func TestConditionForJSONBodySearch(t *testing.T) { }, operator: qbtypes.FilterOperatorLessThan, value: 300, - expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'Int64') < ?", + expectedSQL: `JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') < ?`, expectedError: nil, }, { @@ -489,7 +513,7 @@ func TestConditionForJSONBodySearch(t *testing.T) { }, operator: qbtypes.FilterOperatorContains, value: "200", - expectedSQL: "LOWER(JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'String')) LIKE LOWER(?)", + expectedSQL: `LOWER(JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'String')) LIKE LOWER(?)`, expectedError: nil, }, { @@ -499,7 +523,7 @@ func TestConditionForJSONBodySearch(t *testing.T) { }, operator: qbtypes.FilterOperatorNotContains, value: "200", - expectedSQL: "LOWER(JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'String')) NOT LIKE LOWER(?)", + expectedSQL: `LOWER(JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'String')) NOT LIKE LOWER(?)`, expectedError: nil, }, { @@ -509,7 +533,7 @@ func TestConditionForJSONBodySearch(t *testing.T) { }, operator: qbtypes.FilterOperatorBetween, value: []any{"200", "300"}, - expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'Int64') BETWEEN ? AND ?", + expectedSQL: `JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') BETWEEN ? AND ?`, expectedError: nil, }, { @@ -519,7 +543,7 @@ func TestConditionForJSONBodySearch(t *testing.T) { }, operator: qbtypes.FilterOperatorBetween, value: []any{400, 500}, - expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'Int64') BETWEEN ? AND ?", + expectedSQL: `JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') BETWEEN ? AND ?`, expectedError: nil, }, { @@ -529,7 +553,7 @@ func TestConditionForJSONBodySearch(t *testing.T) { }, operator: qbtypes.FilterOperatorIn, value: []any{"200", "300"}, - expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'Int64') IN (?, ?)", + expectedSQL: `(JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') = ? OR JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') = ?)`, expectedError: nil, }, { @@ -539,7 +563,7 @@ func TestConditionForJSONBodySearch(t *testing.T) { }, operator: qbtypes.FilterOperatorIn, value: []any{401, 404, 500}, - expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'Int64') IN (?, ?, ?)", + expectedSQL: `(JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') = ? OR JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') = ? OR JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') = ?)`, expectedError: nil, }, } diff --git a/pkg/telemetrylogs/const.go b/pkg/telemetrylogs/const.go index 1acc1fcff9..ee3d92af0c 100644 --- a/pkg/telemetrylogs/const.go +++ b/pkg/telemetrylogs/const.go @@ -2,4 +2,5 @@ package telemetrylogs var ( BodyJSONStringSearchPrefix = `body.` + IntrinsicFields = []string{"timestamp", "body", "trace_id", "span_id", "trace_flags", "severity_text", "severity_number"} ) diff --git a/pkg/telemetrylogs/json.go b/pkg/telemetrylogs/json.go index 987018a2c7..89b2f27626 100644 --- a/pkg/telemetrylogs/json.go +++ b/pkg/telemetrylogs/json.go @@ -68,22 +68,36 @@ func inferDataType(value any, operator qbtypes.FilterOperator, key *telemetrytyp return valueType, value } +func getBodyJSONPath(key *telemetrytypes.TelemetryFieldKey) string { + parts := strings.Split(key.Name, ".")[1:] + newParts := []string{} + for _, part := range parts { + if strings.HasSuffix(part, "[*]") { + newParts = append(newParts, fmt.Sprintf(`"%s"[*]`, strings.TrimSuffix(part, "[*]"))) + } else { + newParts = append(newParts, fmt.Sprintf(`"%s"`, part)) + } + } + return strings.Join(newParts, ".") +} + func GetBodyJSONKey(_ context.Context, key *telemetrytypes.TelemetryFieldKey, operator qbtypes.FilterOperator, value any) (string, any) { dataType, value := inferDataType(value, operator, key) - // all body json keys are of the form body. - path := strings.Join(strings.Split(key.Name, ".")[1:], ".") - // for array types, we need to extract the value from the JSON_QUERY if dataType == telemetrytypes.FieldDataTypeArrayInt64 || dataType == telemetrytypes.FieldDataTypeArrayFloat64 || dataType == telemetrytypes.FieldDataTypeArrayString || dataType == telemetrytypes.FieldDataTypeArrayBool || dataType == telemetrytypes.FieldDataTypeArrayNumber { - return fmt.Sprintf("JSONExtract(JSON_QUERY(body, '$.%s'), '%s')", path, dataType.CHDataType()), value + return fmt.Sprintf("JSONExtract(JSON_QUERY(body, '$.%s'), '%s')", getBodyJSONPath(key), dataType.CHDataType()), value } // for all other types, we need to extract the value from the JSON_VALUE - return fmt.Sprintf("JSONExtract(JSON_VALUE(body, '$.%s'), '%s')", path, dataType.CHDataType()), value + return fmt.Sprintf("JSONExtract(JSON_VALUE(body, '$.%s'), '%s')", getBodyJSONPath(key), dataType.CHDataType()), value +} + +func GetBodyJSONKeyForExists(_ context.Context, key *telemetrytypes.TelemetryFieldKey, _ qbtypes.FilterOperator, _ any) string { + return fmt.Sprintf("JSON_EXISTS(body, '$.%s')", getBodyJSONPath(key)) } diff --git a/pkg/telemetrymetadata/condition_builder.go b/pkg/telemetrymetadata/condition_builder.go index 79044f53c2..a029930100 100644 --- a/pkg/telemetrymetadata/condition_builder.go +++ b/pkg/telemetrymetadata/condition_builder.go @@ -76,7 +76,7 @@ func (c *conditionBuilder) ConditionFor( case qbtypes.FilterOperatorRegexp: cond = fmt.Sprintf(`match(%s, %s)`, tblFieldName, sb.Var(value)) case qbtypes.FilterOperatorNotRegexp: - cond = fmt.Sprintf(`not match(%s, %s)`, tblFieldName, sb.Var(value)) + cond = fmt.Sprintf(`NOT match(%s, %s)`, tblFieldName, sb.Var(value)) // in and not in case qbtypes.FilterOperatorIn: @@ -84,13 +84,23 @@ func (c *conditionBuilder) ConditionFor( if !ok { return "", qbtypes.ErrInValues } - cond = sb.In(tblFieldName, values...) + // instead of using IN, we use `=` + `OR` to make use of index + conditions := []string{} + for _, value := range values { + conditions = append(conditions, sb.E(tblFieldName, value)) + } + cond = sb.Or(conditions...) case qbtypes.FilterOperatorNotIn: values, ok := value.([]any) if !ok { return "", qbtypes.ErrInValues } - cond = sb.NotIn(tblFieldName, values...) + // instead of using NOT IN, we use `!=` + `AND` to make use of index + conditions := []string{} + for _, value := range values { + conditions = append(conditions, sb.NE(tblFieldName, value)) + } + cond = sb.And(conditions...) // exists and not exists // in the query builder, `exists` and `not exists` are used for diff --git a/pkg/telemetrytests/agg_rewrite_test.go b/pkg/telemetrytests/agg_rewrite_test.go new file mode 100644 index 0000000000..f4d99281d8 --- /dev/null +++ b/pkg/telemetrytests/agg_rewrite_test.go @@ -0,0 +1,98 @@ +package telemetrytests + +import ( + "testing" + + "github.com/SigNoz/signoz/pkg/querybuilder" + "github.com/SigNoz/signoz/pkg/telemetrylogs" + "github.com/SigNoz/signoz/pkg/telemetrytraces" + "github.com/SigNoz/signoz/pkg/types/telemetrytypes" + "github.com/stretchr/testify/require" +) + +// TestAggRewrite tests rewrite set of aggregation expressions +func TestAggRewrite(t *testing.T) { + fm := telemetrytraces.NewFieldMapper() + cb := telemetrytraces.NewConditionBuilder(fm) + + // Define a comprehensive set of field keys to support all test cases + keys := buildCompleteFieldKeyMap() + + opts := querybuilder.AggExprRewriterOptions{ + FieldMapper: fm, + ConditionBuilder: cb, + FieldKeys: keys, + FullTextColumn: &telemetrytypes.TelemetryFieldKey{ + Name: "body", + }, + JsonBodyPrefix: "body", + JsonKeyToKey: telemetrylogs.GetBodyJSONKey, + RateInterval: 60, + } + + testCases := []struct { + expr string + shouldPass bool + expectedExpr string + expectedArgs []any + expectedErrorContains string + }{ + { + expr: "count()", + shouldPass: true, + expectedExpr: "count()", + }, + { + expr: `countIf(service.name = "redis")`, + shouldPass: true, + expectedExpr: "countIf((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))", + expectedArgs: []any{"redis", true}, + }, + { + expr: `countIf(service.name = "redis" AND status = 200)`, + shouldPass: true, + expectedExpr: "countIf(((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?) AND (attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?)))", + expectedArgs: []any{"redis", true, float64(200), true}, + }, + { + expr: `p05(duration_nano)`, + shouldPass: true, + expectedExpr: "quantile(0.05)(duration_nano)", + }, + { + expr: `rate()`, + shouldPass: true, + expectedExpr: "count()/60", + }, + { + expr: `avg(duration_nano)`, + shouldPass: true, + expectedExpr: "avg(duration_nano)", + }, + { + expr: `sum(total_orders)`, + shouldPass: true, + expectedExpr: "sum(attributes_number['total_orders'])", + }, + } + + rewriter := querybuilder.NewAggExprRewriter(opts) + + for _, tc := range testCases { + t.Run(limitString(tc.expr, 50), func(t *testing.T) { + expr, args, err := rewriter.Rewrite(tc.expr) + if tc.shouldPass { + if err != nil { + t.Errorf("Failed to parse query: %s\nError: %v\n", tc.expr, err) + return + } + // Build the SQL and print it for debugging + require.Equal(t, tc.expectedExpr, expr) + require.Equal(t, tc.expectedArgs, args) + } else { + require.Error(t, err, "Expected error for query: %s", tc.expr) + require.Contains(t, err.Error(), tc.expectedErrorContains) + } + }) + } +} diff --git a/pkg/telemetrytests/filter_expr_logs_body_json_test.go b/pkg/telemetrytests/filter_expr_logs_body_json_test.go new file mode 100644 index 0000000000..1cc1b6de91 --- /dev/null +++ b/pkg/telemetrytests/filter_expr_logs_body_json_test.go @@ -0,0 +1,190 @@ +package telemetrytests + +import ( + "fmt" + "testing" + + "github.com/SigNoz/signoz/pkg/querybuilder" + "github.com/SigNoz/signoz/pkg/telemetrylogs" + "github.com/SigNoz/signoz/pkg/types/telemetrytypes" + "github.com/huandu/go-sqlbuilder" + "github.com/stretchr/testify/require" +) + +// TestFilterExprLogsBodyJSON tests a comprehensive set of query patterns for body JSON search +func TestFilterExprLogsBodyJSON(t *testing.T) { + fm := telemetrylogs.NewFieldMapper() + cb := telemetrylogs.NewConditionBuilder(fm) + + // Define a comprehensive set of field keys to support all test cases + keys := buildCompleteFieldKeyMap() + + opts := querybuilder.FilterExprVisitorOpts{ + FieldMapper: fm, + ConditionBuilder: cb, + FieldKeys: keys, + FullTextColumn: &telemetrytypes.TelemetryFieldKey{ + Name: "body", + }, + JsonBodyPrefix: "body", + JsonKeyToKey: telemetrylogs.GetBodyJSONKey, + } + + testCases := []struct { + category string + query string + shouldPass bool + expectedQuery string + expectedArgs []any + expectedErrorContains string + }{ + { + category: "json", + query: "has(body.requestor_list[*], 'index_service')", + shouldPass: true, + expectedQuery: `WHERE has(JSONExtract(JSON_QUERY(body, '$."requestor_list"[*]'), 'Array(String)'), ?)`, + expectedArgs: []any{"index_service"}, + expectedErrorContains: "", + }, + { + category: "json", + query: "has(body.int_numbers[*], 2)", + shouldPass: true, + expectedQuery: `WHERE has(JSONExtract(JSON_QUERY(body, '$."int_numbers"[*]'), 'Array(Float64)'), ?)`, + expectedArgs: []any{float64(2)}, + expectedErrorContains: "", + }, + { + category: "json", + query: "has(body.bool[*], true)", + shouldPass: true, + expectedQuery: `WHERE has(JSONExtract(JSON_QUERY(body, '$."bool"[*]'), 'Array(Bool)'), ?)`, + expectedArgs: []any{true}, + expectedErrorContains: "", + }, + { + category: "json", + query: "NOT has(body.nested_num[*].float_nums[*], 2.2)", + shouldPass: true, + expectedQuery: `WHERE NOT (has(JSONExtract(JSON_QUERY(body, '$."nested_num"[*]."float_nums"[*]'), 'Array(Float64)'), ?))`, + expectedArgs: []any{float64(2.2)}, + expectedErrorContains: "", + }, + { + category: "json", + query: "body.message = hello", + shouldPass: true, + expectedQuery: `WHERE (JSONExtract(JSON_VALUE(body, '$."message"'), 'String') = ? AND JSON_EXISTS(body, '$."message"'))`, + expectedArgs: []any{"hello"}, + expectedErrorContains: "", + }, + { + category: "json", + query: "body.status = 1", + shouldPass: true, + expectedQuery: `WHERE (JSONExtract(JSON_VALUE(body, '$."status"'), 'Float64') = ? AND JSON_EXISTS(body, '$."status"'))`, + expectedArgs: []any{float64(1)}, + expectedErrorContains: "", + }, + { + category: "json", + query: "body.status = 1.1", + shouldPass: true, + expectedQuery: `WHERE (JSONExtract(JSON_VALUE(body, '$."status"'), 'Float64') = ? AND JSON_EXISTS(body, '$."status"'))`, + expectedArgs: []any{float64(1.1)}, + expectedErrorContains: "", + }, + { + category: "json", + query: "body.boolkey = true", + shouldPass: true, + expectedQuery: `WHERE (JSONExtract(JSON_VALUE(body, '$."boolkey"'), 'Bool') = ? AND JSON_EXISTS(body, '$."boolkey"'))`, + expectedArgs: []any{true}, + expectedErrorContains: "", + }, + { + category: "json", + query: "body.status > 200", + shouldPass: true, + expectedQuery: `WHERE (JSONExtract(JSON_VALUE(body, '$."status"'), 'Float64') > ? AND JSON_EXISTS(body, '$."status"'))`, + expectedArgs: []any{float64(200)}, + expectedErrorContains: "", + }, + { + category: "json", + query: "body.message REGEXP 'a*'", + shouldPass: true, + expectedQuery: `WHERE (match(JSONExtract(JSON_VALUE(body, '$."message"'), 'String'), ?) AND JSON_EXISTS(body, '$."message"'))`, + expectedArgs: []any{"a*"}, + expectedErrorContains: "", + }, + { + category: "json", + query: `body.message CONTAINS "hello 'world'"`, + shouldPass: true, + expectedQuery: `WHERE (LOWER(JSONExtract(JSON_VALUE(body, '$."message"'), 'String')) LIKE LOWER(?) AND JSON_EXISTS(body, '$."message"'))`, + expectedArgs: []any{"%hello 'world'%"}, + expectedErrorContains: "", + }, + { + category: "json", + query: `body.message EXISTS`, + shouldPass: true, + expectedQuery: `WHERE JSON_EXISTS(body, '$."message"')`, + expectedErrorContains: "", + }, + { + category: "json", + query: `body.name IN ('hello', 'world')`, + shouldPass: true, + expectedQuery: `WHERE ((JSONExtract(JSON_VALUE(body, '$."name"'), 'String') = ? OR JSONExtract(JSON_VALUE(body, '$."name"'), 'String') = ?) AND JSON_EXISTS(body, '$."name"'))`, + expectedArgs: []any{"hello", "world"}, + expectedErrorContains: "", + }, + { + category: "json", + query: `body.value IN (200, 300)`, + shouldPass: true, + expectedQuery: `WHERE ((JSONExtract(JSON_VALUE(body, '$."value"'), 'Float64') = ? OR JSONExtract(JSON_VALUE(body, '$."value"'), 'Float64') = ?) AND JSON_EXISTS(body, '$."value"'))`, + expectedArgs: []any{float64(200), float64(300)}, + expectedErrorContains: "", + }, + { + category: "json", + query: "body.key-with-hyphen = true", + shouldPass: true, + expectedQuery: `WHERE (JSONExtract(JSON_VALUE(body, '$."key-with-hyphen"'), 'Bool') = ? AND JSON_EXISTS(body, '$."key-with-hyphen"'))`, + expectedArgs: []any{true}, + expectedErrorContains: "", + }, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("%s: %s", tc.category, limitString(tc.query, 50)), func(t *testing.T) { + + clause, _, err := querybuilder.PrepareWhereClause(tc.query, opts) + + if tc.shouldPass { + if err != nil { + t.Errorf("Failed to parse query: %s\nError: %v\n", tc.query, err) + return + } + + if clause == nil { + t.Errorf("Expected clause for query: %s\n", tc.query) + return + } + + // Build the SQL and print it for debugging + sql, args := clause.BuildWithFlavor(sqlbuilder.ClickHouse) + + require.Equal(t, tc.expectedQuery, sql) + require.Equal(t, tc.expectedArgs, args) + + } else { + require.Error(t, err, "Expected error for query: %s", tc.query) + require.Contains(t, err.Error(), tc.expectedErrorContains) + } + }) + } +} diff --git a/pkg/telemetrytests/filter_expr_logs_test.go b/pkg/telemetrytests/filter_expr_logs_test.go new file mode 100644 index 0000000000..01e1d76c30 --- /dev/null +++ b/pkg/telemetrytests/filter_expr_logs_test.go @@ -0,0 +1,2323 @@ +package telemetrytests + +import ( + "fmt" + "testing" + + "github.com/SigNoz/signoz/pkg/querybuilder" + "github.com/SigNoz/signoz/pkg/telemetrylogs" + "github.com/SigNoz/signoz/pkg/types/telemetrytypes" + "github.com/huandu/go-sqlbuilder" + "github.com/stretchr/testify/require" +) + +// TestFilterExprLogs tests a comprehensive set of query patterns for logs search +func TestFilterExprLogs(t *testing.T) { + fm := telemetrylogs.NewFieldMapper() + cb := telemetrylogs.NewConditionBuilder(fm) + + // Define a comprehensive set of field keys to support all test cases + keys := buildCompleteFieldKeyMap() + + opts := querybuilder.FilterExprVisitorOpts{ + FieldMapper: fm, + ConditionBuilder: cb, + FieldKeys: keys, + FullTextColumn: &telemetrytypes.TelemetryFieldKey{ + Name: "body", + }, + JsonBodyPrefix: "body", + JsonKeyToKey: telemetrylogs.GetBodyJSONKey, + } + + testCases := []struct { + category string + query string + shouldPass bool + expectedQuery string + expectedArgs []any + expectedErrorContains string + }{ + // Single word searches + { + category: "Single word", + query: "download", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"download"}, + expectedErrorContains: "", + }, + { + category: "Single word", + query: "LAMBDA", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"LAMBDA"}, + expectedErrorContains: "", + }, + { + category: "Single word", + query: "AccessDenied", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"AccessDenied"}, + expectedErrorContains: "", + }, + { + category: "Single word", + query: "42069", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"42069"}, + expectedErrorContains: "", + }, + { + category: "Single word", + query: "pulljob", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"pulljob"}, + expectedErrorContains: "", + }, + { + category: "Single word", + query: "", + shouldPass: false, + expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got '<'", + }, + + // Single word searches with spaces + { + category: "Single word with spaces", + query: `" 504 "`, + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{" 504 "}, + expectedErrorContains: "", + }, + { + category: "Single word with spaces", + query: `"Importing "`, + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"Importing "}, + expectedErrorContains: "", + }, + { + category: "Single word with spaces", + query: `"Job ID"`, + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"Job ID"}, + expectedErrorContains: "", + }, + + // Searches with special characters + { + category: "Special characters", + query: "[tracing]", + shouldPass: false, + expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got '['", + }, + { + category: "Special characters", + query: "srikanth@signoz.io", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"srikanth@signoz.io"}, + expectedErrorContains: "", + }, + { + category: "Special characters", + query: "cancel_membership", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"cancel_membership"}, + expectedErrorContains: "", + }, + { + category: "Special characters", + query: `"ERROR: cannot execute update() in a read-only context"`, + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"ERROR: cannot execute update() in a read-only context"}, + expectedErrorContains: "", + }, + { + category: "Special characters", + query: "ERROR: cannot execute update() in a read-only context", + shouldPass: false, + expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got ')'", + }, + { + category: "Special characters", + query: "https://example.com/user/default/0196877a-f01f-785e-a937-5da0a3efbb75", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"https://example.com/user/default/0196877a-f01f-785e-a937-5da0a3efbb75"}, + expectedErrorContains: "", + }, + { + category: "Special characters", + query: "\"STEPS_PER_DAY\"", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"STEPS_PER_DAY"}, + expectedErrorContains: "", + }, + { + category: "Special characters", + query: "#bvn", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"#bvn"}, + expectedErrorContains: "", + }, + { + category: "Special characters", + query: "question?mark", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"question?mark"}, + expectedErrorContains: "", + }, + { + category: "Special characters", + query: "backslash\\\\escape", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"backslash\\\\escape"}, + expectedErrorContains: "", + }, + { + category: "Special characters", + query: "underscore_separator", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"underscore_separator"}, + expectedErrorContains: "", + }, + { + category: "Special characters", + query: "\"Text with [brackets]\"", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"Text with [brackets]"}, + expectedErrorContains: "", + }, + + // Multi word searches + { + category: "Multi word", + query: "Fail to parse", + shouldPass: true, + expectedQuery: "WHERE (match(body, ?) AND match(body, ?) AND match(body, ?))", + expectedArgs: []any{"Fail", "to", "parse"}, + expectedErrorContains: "", + }, + { + category: "Multi word", + query: "Importing file", + shouldPass: true, + expectedQuery: "WHERE (match(body, ?) AND match(body, ?))", + expectedArgs: []any{"Importing", "file"}, + expectedErrorContains: "", + }, + { + category: "Multi word", + query: "sync account status", + shouldPass: true, + expectedQuery: "WHERE (match(body, ?) AND match(body, ?) AND match(body, ?))", + expectedArgs: []any{"sync", "account", "status"}, + expectedErrorContains: "", + }, + { + category: "Multi word", + query: "Download CSV Reports", + shouldPass: true, + expectedQuery: "WHERE (match(body, ?) AND match(body, ?) AND match(body, ?))", + expectedArgs: []any{"Download", "CSV", "Reports"}, + expectedErrorContains: "", + }, + { + category: "Multi word", + query: "Emitted event to the Kafka topic", + shouldPass: true, + expectedQuery: "WHERE (match(body, ?) AND match(body, ?) AND match(body, ?) AND match(body, ?) AND match(body, ?) AND match(body, ?))", + expectedArgs: []any{"Emitted", "event", "to", "the", "Kafka", "topic"}, + expectedErrorContains: "", + }, + { + category: "Multi word", + query: "\"user authentication\" failed", + shouldPass: true, + expectedQuery: "WHERE (match(body, ?) AND match(body, ?))", + expectedArgs: []any{"user authentication", "failed"}, + expectedErrorContains: "", + }, + + // Search for IDs + { + category: "ID search", + query: "250430165501118HIgesxlEb9", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"250430165501118HIgesxlEb9"}, + expectedErrorContains: "", + }, + { + category: "ID search", + query: "d7b9d77aefa95aef19719775c10fda60c28342f23657d1e27304d6c59a3c3004", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"d7b9d77aefa95aef19719775c10fda60c28342f23657d1e27304d6c59a3c3004"}, + expectedErrorContains: "", + }, + { + category: "ID search", + query: "51183870", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"51183870"}, + expectedErrorContains: "", + }, + { + category: "ID search", + query: "79f82635-d014-4f99-adf5-41d31d291ae3", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"79f82635-d014-4f99-adf5-41d31d291ae3"}, + expectedErrorContains: "", + }, + + // Unicode characters in full text + { + category: "Unicode", + query: "café", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"café"}, + expectedErrorContains: "", + }, + { + category: "Unicode", + query: "résumé", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"résumé"}, + expectedErrorContains: "", + }, + { + category: "Unicode", + query: "Россия", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"Россия"}, + expectedErrorContains: "", + }, + { + category: "Unicode", + query: "\"I do not like emojis ❤️\"", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"I do not like emojis ❤️"}, + expectedErrorContains: "", + }, + + // Various number formats + { + category: "Number format", + query: "123", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"123"}, + expectedErrorContains: "", + }, + { + category: "Number format", + query: "3.14159", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"3.14159"}, + expectedErrorContains: "", + }, + { + category: "Number format", + query: "-42", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"-42"}, + expectedErrorContains: "", + }, + { + category: "Number format", + query: "1e6", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"1e6"}, + expectedErrorContains: "", + }, + { + category: "Number format", + query: "+100", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"+100"}, + expectedErrorContains: "", + }, + { + category: "Number format", + query: "0xFF", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"0xFF"}, + expectedErrorContains: "", + }, + + // Typical queries that combine FREETEXT with other constructs + { + category: "FREETEXT with conditions", + query: "critical NOT resolved status=open", + shouldPass: true, + expectedQuery: "WHERE (match(body, ?) AND NOT (match(body, ?)) AND (toString(attributes_number['status']) = ? AND mapContains(attributes_number, 'status') = ?))", + expectedArgs: []any{"critical", "resolved", "open", true}, + expectedErrorContains: "", + }, + { + category: "FREETEXT with conditions", + query: "database error type=mysql", + shouldPass: true, + expectedQuery: "WHERE (match(body, ?) AND match(body, ?) AND (attributes_string['type'] = ? AND mapContains(attributes_string, 'type') = ?))", + expectedArgs: []any{"database", "error", "mysql", true}, + expectedErrorContains: "", + }, + { + category: "FREETEXT with conditions", + query: "\"connection timeout\" duration>30", + shouldPass: true, + expectedQuery: "WHERE (match(body, ?) AND (attributes_number['duration'] > ? AND mapContains(attributes_number, 'duration') = ?))", + expectedArgs: []any{"connection timeout", float64(30), true}, + expectedErrorContains: "", + }, + { + category: "FREETEXT with conditions", + query: "warning level=critical", + shouldPass: true, + expectedQuery: "WHERE (match(body, ?) AND (attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?))", + expectedArgs: []any{"warning", "critical", true}, + expectedErrorContains: "", + }, + { + category: "FREETEXT with conditions", + query: "error service.name=authentication", + shouldPass: true, + expectedQuery: "WHERE (match(body, ?) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))", + expectedArgs: []any{"error", "authentication", true}, + expectedErrorContains: "", + }, + + // fulltext with parenthesized expression + { + category: "FREETEXT with parentheses", + query: "error (status.code=500 OR status.code=503)", + shouldPass: true, + expectedQuery: "WHERE (match(body, ?) AND (((attributes_number['status.code'] = ? AND mapContains(attributes_number, 'status.code') = ?) OR (attributes_number['status.code'] = ? AND mapContains(attributes_number, 'status.code') = ?))))", + expectedArgs: []any{"error", float64(500), true, float64(503), true}, + expectedErrorContains: "", + }, + { + category: "FREETEXT with parentheses", + query: "(status.code=500 OR status.code=503) error", + shouldPass: true, + expectedQuery: "WHERE ((((attributes_number['status.code'] = ? AND mapContains(attributes_number, 'status.code') = ?) OR (attributes_number['status.code'] = ? AND mapContains(attributes_number, 'status.code') = ?))) AND match(body, ?))", + expectedArgs: []any{float64(500), true, float64(503), true, "error"}, + expectedErrorContains: "", + }, + { + category: "FREETEXT with parentheses", + query: "error AND (status.code=500 OR status.code=503)", + shouldPass: true, + expectedQuery: "WHERE (match(body, ?) AND (((attributes_number['status.code'] = ? AND mapContains(attributes_number, 'status.code') = ?) OR (attributes_number['status.code'] = ? AND mapContains(attributes_number, 'status.code') = ?))))", + expectedArgs: []any{"error", float64(500), true, float64(503), true}, + expectedErrorContains: "", + }, + { + category: "FREETEXT with parentheses", + query: "(status.code=500 OR status.code=503) AND error", + shouldPass: true, + expectedQuery: "WHERE ((((attributes_number['status.code'] = ? AND mapContains(attributes_number, 'status.code') = ?) OR (attributes_number['status.code'] = ? AND mapContains(attributes_number, 'status.code') = ?))) AND match(body, ?))", + expectedArgs: []any{float64(500), true, float64(503), true, "error"}, + expectedErrorContains: "", + }, + + // Whitespace with Fulltext + { + category: "Whitespace with FREETEXT", + query: "term1 term2", + shouldPass: true, + expectedQuery: "WHERE (match(body, ?) AND match(body, ?))", + expectedArgs: []any{"term1", "term2"}, + expectedErrorContains: "", + }, + + // Conflicts with the key token, are valid and without additonal tokens, they are searched as FREETEXT + { + category: "Key token conflict", + query: "status.code", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"status.code"}, + expectedErrorContains: "", + }, + { + category: "Key token conflict", + query: "array_field", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"array_field"}, + expectedErrorContains: "", + }, + { + category: "Key token conflict", + query: "user_id.value", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"user_id.value"}, + expectedErrorContains: "", + }, // Could be a key with dot notation or FREETEXT + + // Random set of cases + { + category: "Random cases", + query: "true", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"true"}, + expectedErrorContains: "", + }, // Could be interpreted as boolean or FREETEXT + { + category: "Random cases", + query: "false", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"false"}, + expectedErrorContains: "", + }, // Could be interpreted as boolean or FREETEXT + { + category: "Random cases", + query: "null", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"null"}, + expectedErrorContains: "", + }, // Special value or FREETEXT + { + category: "Random cases", + query: "123abc", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"123abc"}, + expectedErrorContains: "", + }, // Starts with number but contains letters + { + category: "Random cases", + query: "0x123F", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"0x123F"}, + expectedErrorContains: "", + }, // Hex number format + { + category: "Random cases", + query: "1.2.3", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"1.2.3"}, + expectedErrorContains: "", + }, // Version number format + { + category: "Random cases", + query: "a+b-c*d/e", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"a+b-c*d/e"}, + expectedErrorContains: "", + }, // Mathematical expression as FREETEXT + { + category: "Random cases", + query: "http://example.com/path", + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"http://example.com/path"}, + expectedErrorContains: "", + }, // URL as FREETEXT + + // Keyword conflicting + { + category: "Keyword conflict", + query: "and", + shouldPass: false, + expectedQuery: "", + expectedArgs: []any{}, + expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'and'", + }, + { + category: "Keyword conflict", + query: "or", + shouldPass: false, + expectedQuery: "", + expectedArgs: []any{}, + expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'or'", + }, + { + category: "Keyword conflict", + query: "not", + shouldPass: false, + expectedQuery: "", + expectedArgs: []any{}, + expectedErrorContains: "expecting one of {(, ), FREETEXT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got EOF", + }, + { + category: "Keyword conflict", + query: "like", + shouldPass: false, + expectedQuery: "", + expectedArgs: []any{}, + expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'like'", + }, + { + category: "Keyword conflict", + query: "between", + shouldPass: false, + expectedQuery: "", + expectedArgs: []any{}, + expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'between'", + }, + { + category: "Keyword conflict", + query: "in", + shouldPass: false, + expectedQuery: "", + expectedArgs: []any{}, + expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'in'", + }, + { + category: "Keyword conflict", + query: "exists", + shouldPass: false, + expectedQuery: "", + expectedArgs: []any{}, + expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'exists'", + }, + { + category: "Keyword conflict", + query: "regexp", + shouldPass: false, + expectedQuery: "", + expectedArgs: []any{}, + expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'regexp'", + }, + { + category: "Keyword conflict", + query: "contains", + shouldPass: false, + expectedQuery: "", + expectedArgs: []any{}, + expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'contains'", + }, + { + category: "Keyword conflict", + query: "has", + shouldPass: false, + expectedQuery: "", + expectedArgs: []any{}, + expectedErrorContains: "expecting one of {(, )} but got EOF", + }, + { + category: "Keyword conflict", + query: "hasany", + shouldPass: false, + expectedQuery: "", + expectedArgs: []any{}, + expectedErrorContains: "expecting one of {(, )} but got EOF", + }, + { + category: "Keyword conflict", + query: "hasall", + shouldPass: false, + expectedQuery: "", + expectedArgs: []any{}, + expectedErrorContains: "expecting one of {(, )} but got EOF", + }, + + // Boundary of key-operator-value AND full text + { + category: "Key-operator-value boundary", + query: `"not!equal"`, + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"not!equal"}, + expectedErrorContains: "", + }, + { + category: "Key-operator-value boundary", + query: "greater>than", + shouldPass: false, + expectedQuery: "", + expectedArgs: []any{}, + expectedErrorContains: "key `greater` not found", + }, + { + category: "Key-operator-value boundary", + query: `"greater>than"`, + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{"greater>than"}, + expectedErrorContains: "", + }, + { + category: "Key-operator-value boundary", + query: "less ?", + expectedArgs: []any{float64(200)}, + expectedErrorContains: "", + }, + { + category: "Not equals", + query: "status<>200", + shouldPass: true, + expectedQuery: "WHERE attributes_number['status'] <> ?", + expectedArgs: []any{float64(200)}, + expectedErrorContains: "", + }, + { + category: "Not equals", + query: "code!=400", + shouldPass: true, + expectedQuery: "WHERE attributes_number['code'] <> ?", + expectedArgs: []any{float64(400)}, + expectedErrorContains: "", + }, + { + category: "Not equals", + query: "service.name!=\"api\"", + shouldPass: true, + expectedQuery: "WHERE resources_string['service.name'] <> ?", + expectedArgs: []any{"api"}, + expectedErrorContains: "", + }, + { + category: "Not equals", + query: "user.email!=\"user@example.com\"", + shouldPass: true, + expectedQuery: "WHERE attributes_string['user.email'] <> ?", + expectedArgs: []any{"user@example.com"}, + expectedErrorContains: "", + }, + + // Less than + { + category: "Less than", + query: "count<10", + shouldPass: true, + expectedQuery: "WHERE (attributes_number['count'] < ? AND mapContains(attributes_number, 'count') = ?)", + expectedArgs: []any{float64(10), true}, + expectedErrorContains: "", + }, + { + category: "Less than", + query: "duration<1000", + shouldPass: true, + expectedQuery: "WHERE (attributes_number['duration'] < ? AND mapContains(attributes_number, 'duration') = ?)", + expectedArgs: []any{float64(1000), true}, + expectedErrorContains: "", + }, + + // Less than or equal + { + category: "Less than or equal", + query: "count<=10", + shouldPass: true, + expectedQuery: "WHERE (attributes_number['count'] <= ? AND mapContains(attributes_number, 'count') = ?)", + expectedArgs: []any{float64(10), true}, + expectedErrorContains: "", + }, + { + category: "Less than or equal", + query: "duration<=1000", + shouldPass: true, + expectedQuery: "WHERE (attributes_number['duration'] <= ? AND mapContains(attributes_number, 'duration') = ?)", + expectedArgs: []any{float64(1000), true}, + expectedErrorContains: "", + }, + + // Greater than + { + category: "Greater than", + query: "count>10", + shouldPass: true, + expectedQuery: "WHERE (attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?)", + expectedArgs: []any{float64(10), true}, + expectedErrorContains: "", + }, + { + category: "Greater than", + query: "duration>1000", + shouldPass: true, + expectedQuery: "WHERE (attributes_number['duration'] > ? AND mapContains(attributes_number, 'duration') = ?)", + expectedArgs: []any{float64(1000), true}, + expectedErrorContains: "", + }, + + // Greater than or equal + { + category: "Greater than or equal", + query: "count>=10", + shouldPass: true, + expectedQuery: "WHERE (attributes_number['count'] >= ? AND mapContains(attributes_number, 'count') = ?)", + expectedArgs: []any{float64(10), true}, + expectedErrorContains: "", + }, + { + category: "Greater than or equal", + query: "duration>=1000", + shouldPass: true, + expectedQuery: "WHERE (attributes_number['duration'] >= ? AND mapContains(attributes_number, 'duration') = ?)", + expectedArgs: []any{float64(1000), true}, + expectedErrorContains: "", + }, + + // Basic LIKE + { + category: "LIKE operator", + query: "message LIKE \"%error%\"", + shouldPass: true, + expectedQuery: "WHERE (attributes_string['message'] LIKE ? AND mapContains(attributes_string, 'message') = ?)", + expectedArgs: []any{"%error%", true}, + expectedErrorContains: "", + }, + { + category: "LIKE operator", + query: "path LIKE \"/api/%\"", + shouldPass: true, + expectedQuery: "WHERE (attributes_string['path'] LIKE ? AND mapContains(attributes_string, 'path') = ?)", + expectedArgs: []any{"/api/%", true}, + expectedErrorContains: "", + }, + { + category: "LIKE operator", + query: "email LIKE \"%@example.com\"", + shouldPass: true, + expectedQuery: "WHERE (attributes_string['email'] LIKE ? AND mapContains(attributes_string, 'email') = ?)", + expectedArgs: []any{"%@example.com", true}, + expectedErrorContains: "", + }, + { + category: "LIKE operator", + query: "filename LIKE \"%.pdf\"", + shouldPass: true, + expectedQuery: "WHERE (attributes_string['filename'] LIKE ? AND mapContains(attributes_string, 'filename') = ?)", + expectedArgs: []any{"%.pdf", true}, + expectedErrorContains: "", + }, + + // Case-insensitive LIKE (ILIKE) + { + category: "ILIKE operator", + query: "message ILIKE \"%error%\"", + shouldPass: true, + expectedQuery: "WHERE (LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?)", + expectedArgs: []any{"%error%", true}, + expectedErrorContains: "", + }, + { + category: "ILIKE operator", + query: "path ILIKE \"/api/%\"", + shouldPass: true, + expectedQuery: "WHERE (LOWER(attributes_string['path']) LIKE LOWER(?) AND mapContains(attributes_string, 'path') = ?)", + expectedArgs: []any{"/api/%", true}, + expectedErrorContains: "", + }, + { + category: "ILIKE operator", + query: "email ILIKE \"%@EXAMPLE.com\"", + shouldPass: true, + expectedQuery: "WHERE (LOWER(attributes_string['email']) LIKE LOWER(?) AND mapContains(attributes_string, 'email') = ?)", + expectedArgs: []any{"%@EXAMPLE.com", true}, + expectedErrorContains: "", + }, + { + category: "ILIKE operator", + query: "filename ILIKE \"%.PDF\"", + shouldPass: true, + expectedQuery: "WHERE (LOWER(attributes_string['filename']) LIKE LOWER(?) AND mapContains(attributes_string, 'filename') = ?)", + expectedArgs: []any{"%.PDF", true}, + expectedErrorContains: "", + }, + + // NOT LIKE + { + category: "NOT LIKE operator", + query: "message NOT LIKE \"%error%\"", + shouldPass: true, + expectedQuery: "WHERE attributes_string['message'] NOT LIKE ?", + expectedArgs: []any{"%error%"}, + expectedErrorContains: "", + }, + { + category: "NOT LIKE operator", + query: "path NOT LIKE \"/api/%\"", + shouldPass: true, + expectedQuery: "WHERE attributes_string['path'] NOT LIKE ?", + expectedArgs: []any{"/api/%"}, + expectedErrorContains: "", + }, + { + category: "NOT LIKE operator", + query: "email NOT LIKE \"%@example.com\"", + shouldPass: true, + expectedQuery: "WHERE attributes_string['email'] NOT LIKE ?", + expectedArgs: []any{"%@example.com"}, + expectedErrorContains: "", + }, + { + category: "NOT LIKE operator", + query: "filename NOT LIKE \"%.pdf\"", + shouldPass: true, + expectedQuery: "WHERE attributes_string['filename'] NOT LIKE ?", + expectedArgs: []any{"%.pdf"}, + expectedErrorContains: "", + }, + + // NOT ILIKE + { + category: "NOT ILIKE operator", + query: "message NOT ILIKE \"%error%\"", + shouldPass: true, + expectedQuery: "WHERE LOWER(attributes_string['message']) NOT LIKE LOWER(?)", + expectedArgs: []any{"%error%"}, + expectedErrorContains: "", + }, + { + category: "NOT ILIKE operator", + query: "path NOT ILIKE \"/api/%\"", + shouldPass: true, + expectedQuery: "WHERE LOWER(attributes_string['path']) NOT LIKE LOWER(?)", + expectedArgs: []any{"/api/%"}, + expectedErrorContains: "", + }, + { + category: "NOT ILIKE operator", + query: "email NOT ILIKE \"%@EXAMPLE.com\"", + shouldPass: true, + expectedQuery: "WHERE LOWER(attributes_string['email']) NOT LIKE LOWER(?)", + expectedArgs: []any{"%@EXAMPLE.com"}, + expectedErrorContains: "", + }, + { + category: "NOT ILIKE operator", + query: "filename NOT ILIKE \"%.PDF\"", + shouldPass: true, + expectedQuery: "WHERE LOWER(attributes_string['filename']) NOT LIKE LOWER(?)", + expectedArgs: []any{"%.PDF"}, + expectedErrorContains: "", + }, + + // Basic BETWEEN + { + category: "BETWEEN operator", + query: "count BETWEEN 1 AND 10", + shouldPass: true, + expectedQuery: "WHERE (attributes_number['count'] BETWEEN ? AND ? AND mapContains(attributes_number, 'count') = ?)", + expectedArgs: []any{float64(1), float64(10), true}, + expectedErrorContains: "", + }, + { + category: "BETWEEN operator", + query: "duration BETWEEN 100 AND 1000", + shouldPass: true, + expectedQuery: "WHERE (attributes_number['duration'] BETWEEN ? AND ? AND mapContains(attributes_number, 'duration') = ?)", + expectedArgs: []any{float64(100), float64(1000), true}, + expectedErrorContains: "", + }, + { + category: "BETWEEN operator", + query: "amount BETWEEN 0.1 AND 9.9", + shouldPass: true, + expectedQuery: "WHERE (attributes_number['amount'] BETWEEN ? AND ? AND mapContains(attributes_number, 'amount') = ?)", + expectedArgs: []any{0.1, 9.9, true}, + expectedErrorContains: "", + }, + + // NOT BETWEEN + { + category: "NOT BETWEEN operator", + query: "count NOT BETWEEN 1 AND 10", + shouldPass: true, + expectedQuery: "WHERE attributes_number['count'] NOT BETWEEN ? AND ?", + expectedArgs: []any{float64(1), float64(10)}, + expectedErrorContains: "", + }, + { + category: "NOT BETWEEN operator", + query: "duration NOT BETWEEN 100 AND 1000", + shouldPass: true, + expectedQuery: "WHERE attributes_number['duration'] NOT BETWEEN ? AND ?", + expectedArgs: []any{float64(100), float64(1000)}, + expectedErrorContains: "", + }, + { + category: "NOT BETWEEN operator", + query: "amount NOT BETWEEN 0.1 AND 9.9", + shouldPass: true, + expectedQuery: "WHERE attributes_number['amount'] NOT BETWEEN ? AND ?", + expectedArgs: []any{0.1, 9.9}, + expectedErrorContains: "", + }, + + // IN with parentheses + { + category: "IN operator (parentheses)", + query: "status IN (200, 201, 202)", + shouldPass: true, + expectedQuery: "WHERE ((attributes_number['status'] = ? OR attributes_number['status'] = ? OR attributes_number['status'] = ?) AND mapContains(attributes_number, 'status') = ?)", + expectedArgs: []any{float64(200), float64(201), float64(202), true}, + expectedErrorContains: "", + }, + { + category: "IN operator (parentheses)", + query: "error.code IN (404, 500, 503)", + shouldPass: true, + expectedQuery: "WHERE ((attributes_number['error.code'] = ? OR attributes_number['error.code'] = ? OR attributes_number['error.code'] = ?) AND mapContains(attributes_number, 'error.code') = ?)", + expectedArgs: []any{float64(404), float64(500), float64(503), true}, + expectedErrorContains: "", + }, + { + category: "IN operator (parentheses)", + query: "service.name IN (\"api\", \"web\", \"auth\")", + shouldPass: true, + expectedQuery: "WHERE ((resources_string['service.name'] = ? OR resources_string['service.name'] = ? OR resources_string['service.name'] = ?) AND mapContains(resources_string, 'service.name') = ?)", + expectedArgs: []any{"api", "web", "auth", true}, + expectedErrorContains: "", + }, + { + category: "IN operator (parentheses)", + query: "environment IN (\"dev\", \"test\", \"staging\", \"prod\")", + shouldPass: true, + expectedQuery: "WHERE ((resources_string['environment'] = ? OR resources_string['environment'] = ? OR resources_string['environment'] = ? OR resources_string['environment'] = ?) AND mapContains(resources_string, 'environment') = ?)", + expectedArgs: []any{"dev", "test", "staging", "prod", true}, + expectedErrorContains: "", + }, + + // IN with brackets + { + category: "IN operator (brackets)", + query: "status IN [200, 201, 202]", + shouldPass: true, + expectedQuery: "WHERE ((attributes_number['status'] = ? OR attributes_number['status'] = ? OR attributes_number['status'] = ?) AND mapContains(attributes_number, 'status') = ?)", + expectedArgs: []any{float64(200), float64(201), float64(202), true}, + expectedErrorContains: "", + }, + { + category: "IN operator (brackets)", + query: "error.code IN [404, 500, 503]", + shouldPass: true, + expectedQuery: "WHERE ((attributes_number['error.code'] = ? OR attributes_number['error.code'] = ? OR attributes_number['error.code'] = ?) AND mapContains(attributes_number, 'error.code') = ?)", + expectedArgs: []any{float64(404), float64(500), float64(503), true}, + expectedErrorContains: "", + }, + { + category: "IN operator (brackets)", + query: "service.name IN [\"api\", \"web\", \"auth\"]", + shouldPass: true, + expectedQuery: "WHERE ((resources_string['service.name'] = ? OR resources_string['service.name'] = ? OR resources_string['service.name'] = ?) AND mapContains(resources_string, 'service.name') = ?)", + expectedArgs: []any{"api", "web", "auth", true}, + expectedErrorContains: "", + }, + { + category: "IN operator (brackets)", + query: "environment IN [\"dev\", \"test\", \"staging\", \"prod\"]", + shouldPass: true, + expectedQuery: "WHERE ((resources_string['environment'] = ? OR resources_string['environment'] = ? OR resources_string['environment'] = ? OR resources_string['environment'] = ?) AND mapContains(resources_string, 'environment') = ?)", + expectedArgs: []any{"dev", "test", "staging", "prod", true}, + expectedErrorContains: "", + }, + + // NOT IN with parentheses + { + category: "NOT IN operator (parentheses)", + query: "status NOT IN (400, 500)", + shouldPass: true, + expectedQuery: "WHERE (attributes_number['status'] <> ? AND attributes_number['status'] <> ?)", + expectedArgs: []any{float64(400), float64(500)}, + expectedErrorContains: "", + }, + { + category: "NOT IN operator (parentheses)", + query: "error.code NOT IN (401, 403)", + shouldPass: true, + expectedQuery: "WHERE (attributes_number['error.code'] <> ? AND attributes_number['error.code'] <> ?)", + expectedArgs: []any{float64(401), float64(403)}, + expectedErrorContains: "", + }, + { + category: "NOT IN operator (parentheses)", + query: "service.name NOT IN (\"database\", \"cache\")", + shouldPass: true, + expectedQuery: "WHERE (resources_string['service.name'] <> ? AND resources_string['service.name'] <> ?)", + expectedArgs: []any{"database", "cache"}, + expectedErrorContains: "", + }, + { + category: "NOT IN operator (parentheses)", + query: "environment NOT IN (\"prod\")", + shouldPass: true, + expectedQuery: "WHERE (resources_string['environment'] <> ?)", + expectedArgs: []any{"prod"}, + expectedErrorContains: "", + }, + + // NOT IN with brackets + { + category: "NOT IN operator (brackets)", + query: "status NOT IN [400, 500]", + shouldPass: true, + expectedQuery: "WHERE (attributes_number['status'] <> ? AND attributes_number['status'] <> ?)", + expectedArgs: []any{float64(400), float64(500)}, + expectedErrorContains: "", + }, + { + category: "NOT IN operator (brackets)", + query: "error.code NOT IN [401, 403]", + shouldPass: true, + expectedQuery: "WHERE (attributes_number['error.code'] <> ? AND attributes_number['error.code'] <> ?)", + expectedArgs: []any{float64(401), float64(403)}, + expectedErrorContains: "", + }, + { + category: "NOT IN operator (brackets)", + query: "service.name NOT IN [\"database\", \"cache\"]", + shouldPass: true, + expectedQuery: "WHERE (resources_string['service.name'] <> ? AND resources_string['service.name'] <> ?)", + expectedArgs: []any{"database", "cache"}, + expectedErrorContains: "", + }, + { + category: "NOT IN operator (brackets)", + query: "environment NOT IN [\"prod\"]", + shouldPass: true, + expectedQuery: "WHERE (resources_string['environment'] <> ?)", + expectedArgs: []any{"prod"}, + expectedErrorContains: "", + }, + + // Basic EXISTS + { + category: "EXISTS operator", + query: "user.id EXISTS", + shouldPass: true, + expectedQuery: "WHERE mapContains(attributes_string, 'user.id') = ?", + expectedArgs: []any{true}, + expectedErrorContains: "", + }, + { + category: "EXISTS operator", + query: "metadata.version EXISTS", + shouldPass: true, + expectedQuery: "WHERE mapContains(attributes_string, 'metadata.version') = ?", + expectedArgs: []any{true}, + expectedErrorContains: "", + }, + { + category: "EXISTS operator", + query: "request.headers.authorization EXISTS", + shouldPass: true, + expectedQuery: "WHERE mapContains(attributes_string, 'request.headers.authorization') = ?", + expectedArgs: []any{true}, + expectedErrorContains: "", + }, + { + category: "EXISTS operator", + query: "response.body.data EXISTS", + shouldPass: true, + expectedQuery: "WHERE mapContains(attributes_string, 'response.body.data') = ?", + expectedArgs: []any{true}, + expectedErrorContains: "", + }, + + // NOT EXISTS + { + category: "NOT EXISTS operator", + query: "user.id NOT EXISTS", + shouldPass: true, + expectedQuery: "WHERE mapContains(attributes_string, 'user.id') <> ?", + expectedArgs: []any{true}, + expectedErrorContains: "", + }, + { + category: "NOT EXISTS operator", + query: "user.id not exists", + shouldPass: true, + expectedQuery: "WHERE mapContains(attributes_string, 'user.id') <> ?", + expectedArgs: []any{true}, + expectedErrorContains: "", + }, + { + category: "NOT EXISTS operator", + query: "metadata.version NOT EXISTS", + shouldPass: true, + expectedQuery: "WHERE mapContains(attributes_string, 'metadata.version') <> ?", + expectedArgs: []any{true}, + expectedErrorContains: "", + }, + { + category: "NOT EXISTS operator", + query: "request.headers.authorization NOT EXISTS", + shouldPass: true, + expectedQuery: "WHERE mapContains(attributes_string, 'request.headers.authorization') <> ?", + expectedArgs: []any{true}, + expectedErrorContains: "", + }, + { + category: "NOT EXISTS operator", + query: "response.body.data NOT EXISTS", + shouldPass: true, + expectedQuery: "WHERE mapContains(attributes_string, 'response.body.data') <> ?", + expectedArgs: []any{true}, + expectedErrorContains: "", + }, + + // Basic REGEXP + { + category: "REGEXP operator", + query: "message REGEXP \"^ERROR:\"", + shouldPass: true, + expectedQuery: "WHERE (match(attributes_string['message'], ?) AND mapContains(attributes_string, 'message') = ?)", + expectedArgs: []any{"^ERROR:", true}, + expectedErrorContains: "", + }, + { + category: "REGEXP operator", + query: "email REGEXP \"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}$\"", + shouldPass: true, + expectedQuery: "WHERE (match(attributes_string['email'], ?) AND mapContains(attributes_string, 'email') = ?)", + expectedArgs: []any{"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}$", true}, + expectedErrorContains: "", + }, + { + category: "REGEXP operator", + query: "version REGEXP \"^v\\\\d+\\\\.\\\\d+\\\\.\\\\d+$\"", + shouldPass: true, + expectedQuery: "WHERE (match(attributes_string['version'], ?) AND mapContains(attributes_string, 'version') = ?)", + expectedArgs: []any{"^v\\\\d+\\\\.\\\\d+\\\\.\\\\d+$", true}, + expectedErrorContains: "", + }, + { + category: "REGEXP operator", + query: "path REGEXP \"^/api/v\\\\d+/users/\\\\d+$\"", + shouldPass: true, + expectedQuery: "WHERE (match(attributes_string['path'], ?) AND mapContains(attributes_string, 'path') = ?)", + expectedArgs: []any{"^/api/v\\\\d+/users/\\\\d+$", true}, + expectedErrorContains: "", + }, + { + category: "REGEXP operator", + query: `"^\[(INFO|WARN|ERROR|DEBUG)\] .+$"`, + shouldPass: true, + expectedQuery: "WHERE match(body, ?)", + expectedArgs: []any{`^\[(INFO|WARN|ERROR|DEBUG)\] .+$`}, + expectedErrorContains: "", + }, + + // NOT REGEXP + { + category: "NOT REGEXP operator", + query: "message NOT REGEXP \"^ERROR:\"", + shouldPass: true, + expectedQuery: "WHERE NOT match(attributes_string['message'], ?)", + expectedArgs: []any{"^ERROR:"}, + expectedErrorContains: "", + }, + { + category: "NOT REGEXP operator", + query: "email NOT REGEXP \"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}$\"", + shouldPass: true, + expectedQuery: "WHERE NOT match(attributes_string['email'], ?)", + expectedArgs: []any{"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}$"}, + expectedErrorContains: "", + }, + { + category: "NOT REGEXP operator", + query: "version NOT REGEXP \"^v\\\\d+\\\\.\\\\d+\\\\.\\\\d+$\"", + shouldPass: true, + expectedQuery: "WHERE NOT match(attributes_string['version'], ?)", + expectedArgs: []any{"^v\\\\d+\\\\.\\\\d+\\\\.\\\\d+$"}, + expectedErrorContains: "", + }, + { + category: "NOT REGEXP operator", + query: "path NOT REGEXP \"^/api/v\\\\d+/users/\\\\d+$\"", + shouldPass: true, + expectedQuery: "WHERE NOT match(attributes_string['path'], ?)", + expectedArgs: []any{"^/api/v\\\\d+/users/\\\\d+$"}, + expectedErrorContains: "", + }, + + // Basic CONTAINS + { + category: "CONTAINS operator", + query: "message CONTAINS \"error\"", + shouldPass: true, + expectedQuery: "WHERE (LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?)", + expectedArgs: []any{"%error%", true}, + expectedErrorContains: "", + }, + { + category: "CONTAINS operator", + query: "level CONTAINS \"critical\"", + shouldPass: true, + expectedQuery: "WHERE (LOWER(attributes_string['level']) LIKE LOWER(?) AND mapContains(attributes_string, 'level') = ?)", + expectedArgs: []any{"%critical%", true}, + expectedErrorContains: "", + }, + { + category: "CONTAINS operator", + query: "path CONTAINS \"api\"", + shouldPass: true, + expectedQuery: "WHERE (LOWER(attributes_string['path']) LIKE LOWER(?) AND mapContains(attributes_string, 'path') = ?)", + expectedArgs: []any{"%api%", true}, + expectedErrorContains: "", + }, + + // NOT CONTAINS + { + category: "NOT CONTAINS operator", + query: "message NOT CONTAINS \"error\"", + shouldPass: true, + expectedQuery: "WHERE LOWER(attributes_string['message']) NOT LIKE LOWER(?)", + expectedArgs: []any{"%error%"}, + expectedErrorContains: "", + }, + { + category: "NOT CONTAINS operator", + query: "level NOT CONTAINS \"critical\"", + shouldPass: true, + expectedQuery: "WHERE LOWER(attributes_string['level']) NOT LIKE LOWER(?)", + expectedArgs: []any{"%critical%"}, + expectedErrorContains: "", + }, + { + category: "NOT CONTAINS operator", + query: "path NOT CONTAINS \"api\"", + shouldPass: true, + expectedQuery: "WHERE LOWER(attributes_string['path']) NOT LIKE LOWER(?)", + expectedArgs: []any{"%api%"}, + expectedErrorContains: "", + }, + + // Basic materialized key + { + category: "Materialized key", + query: "materialized.key.name=\"test\"", + shouldPass: true, + expectedQuery: "WHERE (`attribute_string_materialized$$key$$name` = ? AND `attribute_string_materialized$$key$$name_exists` = ?)", + expectedArgs: []any{"test", true}, + expectedErrorContains: "", + }, + + // Explicit AND + { + category: "Explicit AND", + query: "status=200 AND service.name=\"api\"", + shouldPass: true, + expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))", + expectedArgs: []any{float64(200), true, "api", true}, + expectedErrorContains: "", + }, + { + category: "Explicit AND", + query: "count>0 AND duration<1000", + shouldPass: true, + expectedQuery: "WHERE ((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?) AND (attributes_number['duration'] < ? AND mapContains(attributes_number, 'duration') = ?))", + expectedArgs: []any{float64(0), true, float64(1000), true}, + expectedErrorContains: "", + }, + { + category: "Explicit AND", + query: "message CONTAINS \"error\" AND level=\"ERROR\"", + shouldPass: true, + expectedQuery: "WHERE ((LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?) AND (attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?))", + expectedArgs: []any{"%error%", true, "ERROR", true}, + expectedErrorContains: "", + }, + + // Explicit OR + { + category: "Explicit OR", + query: "status=200 OR status=201", + shouldPass: true, + expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) OR (attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?))", + expectedArgs: []any{float64(200), true, float64(201), true}, + expectedErrorContains: "", + }, + { + category: "Explicit OR", + query: "service.name=\"api\" OR service.name=\"web\"", + shouldPass: true, + expectedQuery: "WHERE ((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?) OR (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))", + expectedArgs: []any{"api", true, "web", true}, + expectedErrorContains: "", + }, + { + category: "Explicit OR", + query: "count<10 OR count>100", + shouldPass: true, + expectedQuery: "WHERE ((attributes_number['count'] < ? AND mapContains(attributes_number, 'count') = ?) OR (attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?))", + expectedArgs: []any{float64(10), true, float64(100), true}, + expectedErrorContains: "", + }, + + // NOT with various expressions + { + category: "NOT with expressions", + query: "NOT status=200", + shouldPass: true, + expectedQuery: "WHERE NOT ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?))", + expectedArgs: []any{float64(200), true}, + expectedErrorContains: "", + }, + { + category: "NOT with expressions", + query: "NOT service.name=\"api\"", + shouldPass: true, + expectedQuery: "WHERE NOT ((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))", + expectedArgs: []any{"api", true}, + expectedErrorContains: "", + }, + { + category: "NOT with expressions", + query: "NOT count>10", + shouldPass: true, + expectedQuery: "WHERE NOT ((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?))", + expectedArgs: []any{float64(10), true}, + expectedErrorContains: "", + }, + + // AND + OR combinations + { + category: "AND + OR combinations", + query: "status=200 AND (service.name=\"api\" OR service.name=\"web\")", + shouldPass: true, + expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND (((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?) OR (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))))", + expectedArgs: []any{float64(200), true, "api", true, "web", true}, + expectedErrorContains: "", + }, + { + category: "AND + OR combinations", + query: "(count>10 AND count<100) OR (duration>1000 AND duration<5000)", + shouldPass: true, + expectedQuery: "WHERE ((((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?) AND (attributes_number['count'] < ? AND mapContains(attributes_number, 'count') = ?))) OR (((attributes_number['duration'] > ? AND mapContains(attributes_number, 'duration') = ?) AND (attributes_number['duration'] < ? AND mapContains(attributes_number, 'duration') = ?))))", + expectedArgs: []any{float64(10), true, float64(100), true, float64(1000), true, float64(5000), true}, + expectedErrorContains: "", + }, + { + category: "AND + OR combinations", + query: "level=\"ERROR\" OR (level=\"WARN\" AND message CONTAINS \"timeout\")", + shouldPass: true, + expectedQuery: "WHERE ((attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?) OR (((attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?) AND (LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?))))", + expectedArgs: []any{"ERROR", true, "WARN", true, "%timeout%", true}, + expectedErrorContains: "", + }, + + // AND + NOT combinations + { + category: "AND + NOT combinations", + query: "status=200 AND NOT service.name=\"api\"", + shouldPass: true, + expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND NOT ((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)))", + expectedArgs: []any{float64(200), true, "api", true}, + expectedErrorContains: "", + }, + { + category: "AND + NOT combinations", + query: "count>0 AND NOT error.code EXISTS", + shouldPass: true, + expectedQuery: "WHERE ((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?) AND NOT (mapContains(attributes_number, 'error.code') = ?))", + expectedArgs: []any{float64(0), true, true}, + expectedErrorContains: "", + }, + + // OR + NOT combinations + { + category: "OR + NOT combinations", + query: "NOT status=200 OR NOT service.name=\"api\"", + shouldPass: true, + expectedQuery: "WHERE (NOT ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?)) OR NOT ((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)))", + expectedArgs: []any{float64(200), true, "api", true}, + expectedErrorContains: "", + }, + { + category: "OR + NOT combinations", + query: "NOT count>0 OR NOT error.code EXISTS", + shouldPass: true, + expectedQuery: "WHERE (NOT ((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?)) OR NOT (mapContains(attributes_number, 'error.code') = ?))", + expectedArgs: []any{float64(0), true, true}, + expectedErrorContains: "", + }, + + // AND + OR + NOT combinations + { + category: "AND + OR + NOT combinations", + query: "status=200 AND (service.name=\"api\" OR NOT duration>1000)", + shouldPass: true, + expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND (((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?) OR NOT ((attributes_number['duration'] > ? AND mapContains(attributes_number, 'duration') = ?)))))", + expectedArgs: []any{float64(200), true, "api", true, float64(1000), true}, + expectedErrorContains: "", + }, + { + category: "AND + OR + NOT combinations", + query: "(level=\"ERROR\" OR level=\"FATAL\") AND NOT message CONTAINS \"expected\"", + shouldPass: true, + expectedQuery: "WHERE ((((attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?) OR (attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?))) AND NOT ((LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?)))", + expectedArgs: []any{"ERROR", true, "FATAL", true, "%expected%", true}, + expectedErrorContains: "", + }, + { + category: "AND + OR + NOT combinations", + query: "NOT (status=200 AND service.name=\"api\") OR count>0", + shouldPass: true, + expectedQuery: "WHERE (NOT ((((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)))) OR (attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?))", + expectedArgs: []any{float64(200), true, "api", true, float64(0), true}, + expectedErrorContains: "", + }, + + // Multiple expressions without explicit AND + { + category: "Implicit AND", + query: "status=200 service.name=\"api\"", + shouldPass: true, + expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))", + expectedArgs: []any{float64(200), true, "api", true}, + expectedErrorContains: "", + }, + { + category: "Implicit AND", + query: "count>0 duration<1000", + shouldPass: true, + expectedQuery: "WHERE ((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?) AND (attributes_number['duration'] < ? AND mapContains(attributes_number, 'duration') = ?))", + expectedArgs: []any{float64(0), true, float64(1000), true}, + expectedErrorContains: "", + }, + { + category: "Implicit AND", + query: "message CONTAINS \"error\" level=\"ERROR\"", + shouldPass: true, + expectedQuery: "WHERE ((LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?) AND (attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?))", + expectedArgs: []any{"%error%", true, "ERROR", true}, + expectedErrorContains: "", + }, + + // Mixed implicit and explicit AND + { + category: "Mixed implicit/explicit AND", + query: "status=200 AND service.name=\"api\" duration<1000", + shouldPass: true, + expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?) AND (attributes_number['duration'] < ? AND mapContains(attributes_number, 'duration') = ?))", + expectedArgs: []any{float64(200), true, "api", true, float64(1000), true}, + expectedErrorContains: "", + }, + { + category: "Mixed implicit/explicit AND", + query: "count>0 level=\"ERROR\" AND message CONTAINS \"error\"", + shouldPass: true, + expectedQuery: "WHERE ((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?) AND (attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?) AND (LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?))", + expectedArgs: []any{float64(0), true, "ERROR", true, "%error%", true}, + expectedErrorContains: "", + }, + + // Simple grouping + { + category: "Simple grouping", + query: "(status=200)", + shouldPass: true, + expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?))", + expectedArgs: []any{float64(200), true}, + expectedErrorContains: "", + }, + { + category: "Simple grouping", + query: "service.name=\"api\"", + shouldPass: true, + expectedQuery: "WHERE (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)", + expectedArgs: []any{"api", true}, + expectedErrorContains: "", + }, + { + category: "Simple grouping", + query: "(count>0)", + shouldPass: true, + expectedQuery: "WHERE ((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?))", + expectedArgs: []any{float64(0), true}, + expectedErrorContains: "", + }, + + // Nested grouping + { + category: "Nested grouping", + query: "((status=200))", + shouldPass: true, + expectedQuery: "WHERE (((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?)))", + expectedArgs: []any{float64(200), true}, + expectedErrorContains: "", + }, + { + category: "Nested grouping", + query: "(((service.name=\"api\")))", + shouldPass: true, + expectedQuery: "WHERE ((((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))))", + expectedArgs: []any{"api", true}, + expectedErrorContains: "", + }, + { + category: "Nested grouping", + query: "((count>0) AND (duration<1000))", + shouldPass: true, + expectedQuery: "WHERE ((((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?)) AND ((attributes_number['duration'] < ? AND mapContains(attributes_number, 'duration') = ?))))", + expectedArgs: []any{float64(0), true, float64(1000), true}, + expectedErrorContains: "", + }, + + // Complex nested grouping + { + category: "Complex nested grouping", + query: "(status=200 AND (service.name=\"api\" OR service.name=\"web\"))", + shouldPass: true, + expectedQuery: "WHERE (((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND (((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?) OR (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)))))", + expectedArgs: []any{float64(200), true, "api", true, "web", true}, + expectedErrorContains: "", + }, + { + category: "Complex nested grouping", + query: "((count>0 AND count<100) OR (duration>1000 AND duration<5000))", + shouldPass: true, + expectedQuery: "WHERE (((((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?) AND (attributes_number['count'] < ? AND mapContains(attributes_number, 'count') = ?))) OR (((attributes_number['duration'] > ? AND mapContains(attributes_number, 'duration') = ?) AND (attributes_number['duration'] < ? AND mapContains(attributes_number, 'duration') = ?)))))", + expectedArgs: []any{float64(0), true, float64(100), true, float64(1000), true, float64(5000), true}, + expectedErrorContains: "", + }, + { + category: "Complex nested grouping", + query: "(level=\"ERROR\" OR (level=\"WARN\" AND (message CONTAINS \"timeout\" OR message CONTAINS \"connection\")))", + shouldPass: true, + expectedQuery: "WHERE (((attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?) OR (((attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?) AND (((LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?) OR (LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?)))))))", + expectedArgs: []any{"ERROR", true, "WARN", true, "%timeout%", true, "%connection%", true}, + expectedErrorContains: "", + }, + + // Deep nesting with mixed operators + { + category: "Deep nesting", + query: "(((status=200 OR status=201) AND service.name=\"api\") OR ((status=202 OR status=203) AND service.name=\"web\"))", + shouldPass: true, + expectedQuery: "WHERE (((((((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) OR (attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?))) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))) OR (((((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) OR (attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?))) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)))))", + expectedArgs: []any{float64(200), true, float64(201), true, "api", true, float64(202), true, float64(203), true, "web", true}, + expectedErrorContains: "", + }, + { + category: "Deep nesting", + query: "(count>0 AND ((duration<1000 AND service.name=\"api\") OR (duration<500 AND service.name=\"web\")))", + shouldPass: true, + expectedQuery: "WHERE (((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?) AND (((((attributes_number['duration'] < ? AND mapContains(attributes_number, 'duration') = ?) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))) OR (((attributes_number['duration'] < ? AND mapContains(attributes_number, 'duration') = ?) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)))))))", + expectedArgs: []any{float64(0), true, float64(1000), true, "api", true, float64(500), true, "web", true}, + expectedErrorContains: "", + }, + + // String values with different quote styles + { + category: "String quote styles", + query: "service.name=\"api\"", + shouldPass: true, + expectedQuery: "WHERE (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)", + expectedArgs: []any{"api", true}, + expectedErrorContains: "", + }, + { + category: "String quote styles", + query: "service.name='api'", + shouldPass: true, + expectedQuery: "WHERE (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)", + expectedArgs: []any{"api", true}, + expectedErrorContains: "", + }, + { + category: "String quote styles", + query: "message=\"This is a \\\"quoted\\\" message\"", + shouldPass: true, + expectedQuery: "WHERE (attributes_string['message'] = ? AND mapContains(attributes_string, 'message') = ?)", + expectedArgs: []any{"This is a \\\"quoted\\\" message", true}, + expectedErrorContains: "", + }, + { + category: "String quote styles", + query: "message='This is a \\'quoted\\' message'", + shouldPass: true, + expectedQuery: "WHERE (attributes_string['message'] = ? AND mapContains(attributes_string, 'message') = ?)", + expectedArgs: []any{"This is a \\'quoted\\' message", true}, + expectedErrorContains: "", + }, + + // Numeric values + { + category: "Numeric values", + query: "status=200", + shouldPass: true, + expectedQuery: "WHERE (attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?)", + expectedArgs: []any{float64(200), true}, + expectedErrorContains: "", + }, + { + category: "Numeric values", + query: "count=0", + shouldPass: true, + expectedQuery: "WHERE (attributes_number['count'] = ? AND mapContains(attributes_number, 'count') = ?)", + expectedArgs: []any{float64(0), true}, + expectedErrorContains: "", + }, + { + category: "Numeric values", + query: "duration=1000.5", + shouldPass: true, + expectedQuery: "WHERE (attributes_number['duration'] = ? AND mapContains(attributes_number, 'duration') = ?)", + expectedArgs: []any{float64(1000.5), true}, + expectedErrorContains: "", + }, + { + category: "Numeric values", + query: "amount=-10.25", + shouldPass: true, + expectedQuery: "WHERE (attributes_number['amount'] = ? AND mapContains(attributes_number, 'amount') = ?)", + expectedArgs: []any{float64(-10.25), true}, + expectedErrorContains: "", + }, + + // Boolean values + { + category: "Boolean values", + query: "isEnabled=true", + shouldPass: true, + expectedQuery: "WHERE (attributes_bool['isEnabled'] = ? AND mapContains(attributes_bool, 'isEnabled') = ?)", + expectedArgs: []any{true, true}, + expectedErrorContains: "", + }, + { + category: "Boolean values", + query: "isDisabled=false", + shouldPass: true, + expectedQuery: "WHERE (attributes_bool['isDisabled'] = ? AND mapContains(attributes_bool, 'isDisabled') = ?)", + expectedArgs: []any{false, true}, + expectedErrorContains: "", + }, + { + category: "Boolean values", + query: "is_valid=TRUE", + shouldPass: true, + expectedQuery: "WHERE (attributes_bool['is_valid'] = ? AND mapContains(attributes_bool, 'is_valid') = ?)", + expectedArgs: []any{true, true}, + expectedErrorContains: "", + }, + { + category: "Boolean values", + query: "is_invalid=FALSE", + shouldPass: true, + expectedQuery: "WHERE (attributes_bool['is_invalid'] = ? AND mapContains(attributes_bool, 'is_invalid') = ?)", + expectedArgs: []any{false, true}, + expectedErrorContains: "", + }, + + // Null and undefined values, there is no such type as "null" or "undefined" + // they are just strings + { + category: "Null values", + query: "value=null", + shouldPass: true, + expectedQuery: "WHERE (attributes_string['value'] = ? AND mapContains(attributes_string, 'value') = ?)", + expectedArgs: []any{"null", true}, + expectedErrorContains: "", + }, + { + category: "Null values", + query: "value=undefined", + shouldPass: true, + expectedQuery: "WHERE (attributes_string['value'] = ? AND mapContains(attributes_string, 'value') = ?)", + expectedArgs: []any{"undefined", true}, + expectedErrorContains: "", + }, + + // Nested object paths + { + category: "Nested object paths", + query: "user.profile.name=\"John\"", + shouldPass: true, + expectedQuery: "WHERE (attributes_string['user.profile.name'] = ? AND mapContains(attributes_string, 'user.profile.name') = ?)", + expectedArgs: []any{"John", true}, + expectedErrorContains: "", + }, + { + category: "Nested object paths", + query: "request.headers.authorization EXISTS", + shouldPass: true, + expectedQuery: "WHERE mapContains(attributes_string, 'request.headers.authorization') = ?", + expectedArgs: []any{true}, + expectedErrorContains: "", + }, + { + category: "Nested object paths", + query: "response.body.data.items[].id=123", + shouldPass: false, + expectedQuery: "", + expectedArgs: nil, + expectedErrorContains: "key `response.body.data.items[].id` not found", + }, + { + category: "Nested object paths", + query: "metadata.dimensions.width>1000", + shouldPass: true, + expectedQuery: "WHERE (attributes_number['metadata.dimensions.width'] > ? AND mapContains(attributes_number, 'metadata.dimensions.width') = ?)", + expectedArgs: []any{float64(1000), true}, + expectedErrorContains: "", + }, + + {category: "Only keywords", query: "AND", shouldPass: false, expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'AND'"}, + {category: "Only keywords", query: "OR", shouldPass: false, expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'OR'"}, + {category: "Only keywords", query: "NOT", shouldPass: false, expectedErrorContains: "expecting one of {(, ), FREETEXT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got EOF"}, + + {category: "Only functions", query: "has", shouldPass: false, expectedErrorContains: "expecting one of {(, )} but got EOF"}, + {category: "Only functions", query: "hasAny", shouldPass: false, expectedErrorContains: "expecting one of {(, )} but got EOF"}, + {category: "Only functions", query: "hasAll", shouldPass: false, expectedErrorContains: "expecting one of {(, )} but got EOF"}, + + {category: "Values without keys", query: "=200", shouldPass: false, expectedErrorContains: "but got '='"}, + {category: "Values without keys", query: "=\"api\"", shouldPass: false, expectedErrorContains: "but got '='"}, + {category: "Values without keys", query: ">10", shouldPass: false, expectedErrorContains: "but got '>'"}, + {category: "Values without keys", query: "BETWEEN 1 AND 10", shouldPass: false, expectedErrorContains: "but got 'BETWEEN'"}, + + // Testing NOT > AND > OR precedence + { + category: "Operator precedence", + query: "NOT status=200 AND service.name=\"api\"", + shouldPass: true, + expectedQuery: "WHERE (NOT ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?)) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))", + expectedArgs: []any{float64(200), true, "api", true}, // Should be (NOT status=200) AND service.name="api" + }, + { + category: "Operator precedence", + query: "status=200 AND service.name=\"api\" OR service.name=\"web\"", + shouldPass: true, + expectedQuery: "WHERE (((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)) OR (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))", + expectedArgs: []any{float64(200), true, "api", true, "web", true}, // Should be (status=200 AND service.name="api") OR service.name="web" + }, + { + category: "Operator precedence", + query: "NOT status=200 OR NOT service.name=\"api\"", + shouldPass: true, + expectedQuery: "WHERE (NOT ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?)) OR NOT ((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)))", + expectedArgs: []any{float64(200), true, "api", true}, // Should be (NOT status=200) OR (NOT service.name="api") + }, + { + category: "Operator precedence", + query: "status=200 OR service.name=\"api\" AND level=\"ERROR\"", + shouldPass: true, + expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) OR ((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?) AND (attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?)))", + expectedArgs: []any{float64(200), true, "api", true, "ERROR", true}, // Should be status=200 OR (service.name="api" AND level="ERROR") + }, + + // Different whitespace patterns + { + category: "Whitespace patterns", + query: "status=200AND service.name=\"api\"", + shouldPass: false, + expectedQuery: "", + expectedArgs: nil, + expectedErrorContains: "expecting one of {boolean, number, quoted text} but got '200AND'", + }, + { + category: "Whitespace patterns", + query: "status=200ANDservice.name=\"api\"", + shouldPass: false, + expectedQuery: "", + expectedArgs: nil, + expectedErrorContains: "expecting one of {boolean, number, quoted text} but got '200ANDservice.name'", + }, + { + category: "Whitespace patterns", + query: "status=200 AND service.name=\"api\"", + shouldPass: true, + expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))", + expectedArgs: []any{float64(200), true, "api", true}, // Multiple spaces + }, + + // More Unicode characters + { + category: "Unicode", + query: "message=\"café\"", + shouldPass: true, + expectedQuery: "WHERE (attributes_string['message'] = ? AND mapContains(attributes_string, 'message') = ?)", + expectedArgs: []any{"café", true}, + }, + { + category: "Unicode", + query: "user.name=\"Jörg Müller\"", + shouldPass: true, + expectedQuery: "WHERE (attributes_string['user.name'] = ? AND mapContains(attributes_string, 'user.name') = ?)", + expectedArgs: []any{"Jörg Müller", true}, + }, + { + category: "Unicode", + query: "location=\"東京\"", + shouldPass: true, + expectedQuery: "WHERE (attributes_string['location'] = ? AND mapContains(attributes_string, 'location') = ?)", + expectedArgs: []any{"東京", true}, + }, + { + category: "Unicode", + query: "description=\"Это тестовое сообщение\"", + shouldPass: true, + expectedQuery: "WHERE (attributes_string['description'] = ? AND mapContains(attributes_string, 'description') = ?)", + expectedArgs: []any{"Это тестовое сообщение", true}, + }, + + // Special characters + { + category: "Special characters", + query: "path=\"/special/!@#$%^&*()\"", + shouldPass: true, + expectedQuery: "WHERE (attributes_string['path'] = ? AND mapContains(attributes_string, 'path') = ?)", + expectedArgs: []any{"/special/!@#$%^&*()", true}, + }, + { + category: "Special characters", + query: "query=\"?param1=value1¶m2=value2\"", + shouldPass: true, + expectedQuery: "WHERE (attributes_string['query'] = ? AND mapContains(attributes_string, 'query') = ?)", + expectedArgs: []any{"?param1=value1¶m2=value2", true}, + }, + + // Special characters in keys + { + category: "Special characters in keys", + query: "special-key=\"value\"", + shouldPass: true, + expectedQuery: "WHERE (attributes_string['special-key'] = ? AND mapContains(attributes_string, 'special-key') = ?)", + expectedArgs: []any{"value", true}, // With hyphen + }, + { + category: "Special characters in keys", + query: "special.key=\"value\"", + shouldPass: true, + expectedQuery: "WHERE (attributes_string['special.key'] = ? AND mapContains(attributes_string, 'special.key') = ?)", + expectedArgs: []any{"value", true}, // With dot + }, + { + category: "Special characters in keys", + query: "special_key=\"value\"", + shouldPass: true, + expectedQuery: "WHERE (attributes_string['special_key'] = ? AND mapContains(attributes_string, 'special_key') = ?)", + expectedArgs: []any{"value", true}, // With underscore + }, + + // Using operator keywords as keys + { + category: "Operator keywords as keys", + query: "and=true", + shouldPass: false, + expectedQuery: "", + expectedArgs: nil, + expectedErrorContains: "line 1:0 expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'and'", + }, + { + category: "Operator keywords as keys", + query: "or=false", + shouldPass: false, + expectedQuery: "", + expectedArgs: nil, + expectedErrorContains: "line 1:0 expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'or'", + }, + { + category: "Operator keywords as keys", + query: "not=\"value\"", + shouldPass: false, + expectedQuery: "", + expectedArgs: nil, + expectedErrorContains: "line 1:3 expecting one of {(, ), FREETEXT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got '='", + }, + { + category: "Operator keywords as keys", + query: "between=10", + shouldPass: false, + expectedQuery: "", + expectedArgs: nil, + expectedErrorContains: "line 1:0 expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'between'", + }, + { + category: "Operator keywords as keys", + query: "in=\"list\"", + shouldPass: false, + expectedQuery: "", + expectedArgs: nil, + expectedErrorContains: "line 1:0 expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'in'", + }, + + // Using function keywords as keys + { + category: "Function keywords as keys", + query: "has=\"function\"", + shouldPass: false, + expectedQuery: "", + expectedArgs: nil, + expectedErrorContains: "expecting one of {(, )} but got '='", + }, + { + category: "Function keywords as keys", + query: "hasAny=\"function\"", + shouldPass: false, + expectedQuery: "", + expectedArgs: nil, + expectedErrorContains: "expecting one of {(, )} but got '='", + }, + { + category: "Function keywords as keys", + query: "hasAll=\"function\"", + shouldPass: false, + expectedQuery: "", + expectedArgs: nil, + expectedErrorContains: "expecting one of {(, )} but got '='", + }, + + // Common filter patterns + { + category: "Common filters", + query: "status IN (\"pending\", \"processing\", \"completed\") AND NOT is_deleted=true", + shouldPass: true, + expectedQuery: "WHERE (((attributes_number['status'] = ? OR attributes_number['status'] = ? OR attributes_number['status'] = ?) AND mapContains(attributes_number, 'status') = ?) AND NOT ((attributes_bool['is_deleted'] = ? AND mapContains(attributes_bool, 'is_deleted') = ?)))", + expectedArgs: []any{"pending", "processing", "completed", true, true, true}, + }, + { + category: "Common filters", + query: "(first_name LIKE \"John%\" OR last_name LIKE \"Smith%\") AND age>=18", + shouldPass: true, + expectedQuery: "WHERE ((((attributes_string['first_name'] LIKE ? AND mapContains(attributes_string, 'first_name') = ?) OR (attributes_string['last_name'] LIKE ? AND mapContains(attributes_string, 'last_name') = ?))) AND (attributes_number['age'] >= ? AND mapContains(attributes_number, 'age') = ?))", + expectedArgs: []any{"John%", true, "Smith%", true, float64(18), true}, + }, + { + category: "Common filters", + query: "user_id=12345 AND (subscription_type=\"premium\" OR has(features, \"advanced\"))", + shouldPass: false, + expectedQuery: "", + expectedArgs: nil, + expectedErrorContains: "key `user_id` not found", + }, + + // More common filter patterns + { + category: "More common filters", + query: "service.name=\"api\" AND (status>=500 OR duration>1000) AND NOT message CONTAINS \"expected\"", + shouldPass: true, + expectedQuery: "WHERE ((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?) AND (((attributes_number['status'] >= ? AND mapContains(attributes_number, 'status') = ?) OR (attributes_number['duration'] > ? AND mapContains(attributes_number, 'duration') = ?))) AND NOT ((LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?)))", + expectedArgs: []any{"api", true, float64(500), true, float64(1000), true, "%expected%", true}, + }, + + // Edge cases + { + category: "empty array", + query: "key IN ()", + shouldPass: false, + expectedQuery: "", + expectedArgs: nil, + expectedErrorContains: "expecting one of {boolean, number, quoted text}", + }, + { + category: "empty array", + query: "key IN []", + shouldPass: false, + expectedQuery: "", + expectedArgs: nil, + expectedErrorContains: "expecting one of {boolean, number, quoted text}", + }, + { + category: "empty array", + query: "key NOT IN ()", + shouldPass: false, + expectedQuery: "", + expectedArgs: nil, + expectedErrorContains: "expecting one of {boolean, number, quoted text}", + }, + { + category: "empty array", + query: "key NOT IN []", + shouldPass: false, + expectedQuery: "", + expectedArgs: nil, + expectedErrorContains: "expecting one of {boolean, number, quoted text}", + }, + + // Values with escaping + { + category: "Escaped values", + query: "message=\"Line 1\\nLine 2\\tTabbed\\rCarriage return\"", + shouldPass: true, + expectedQuery: "WHERE (attributes_string['message'] = ? AND mapContains(attributes_string, 'message') = ?)", + expectedArgs: []any{"Line 1\\nLine 2\\tTabbed\\rCarriage return", true}, + }, + { + category: "Escaped values", + query: "path=\"C:\\\\Program Files\\\\Application\"", + shouldPass: true, + expectedQuery: "WHERE (attributes_string['path'] = ? AND mapContains(attributes_string, 'path') = ?)", + expectedArgs: []any{"C:\\\\Program Files\\\\Application", true}, + }, + { + category: "Escaped values", + query: "path=\"^prefix\\\\.suffix$\\\\d+\\\\w+\"", + shouldPass: true, + expectedQuery: "WHERE (attributes_string['path'] = ? AND mapContains(attributes_string, 'path') = ?)", + expectedArgs: []any{"^prefix\\\\.suffix$\\\\d+\\\\w+", true}, + }, + + // Inconsistent/unusual whitespace + { + category: "Unusual whitespace", + query: "status = 200 AND service.name = \"api\"", + shouldPass: true, + expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))", + expectedArgs: []any{float64(200), true, "api", true}, + }, + { + category: "Unusual whitespace", + query: "status=200AND service.name=\"api\"", + shouldPass: false, + expectedQuery: "", + expectedArgs: nil, + expectedErrorContains: "expecting one of {boolean, number, quoted text} but got '200AND'", + }, + { + category: "Unusual whitespace", + query: "status=200ANDservice.name=\"api\"", + shouldPass: false, + expectedQuery: "", + expectedArgs: nil, + expectedErrorContains: "expecting one of {boolean, number, quoted text} but got '200ANDservice.name'", + }, + + // Very complex query example + { + category: "Complex query example", + query: ` + ( + ( + (status>=200 AND status<300) OR + (status>=400 AND status<500 AND NOT status=404) + ) AND + ( + service.name IN ("api", "web", "auth") OR + ( + service.type="internal" AND + NOT service.deprecated=true + ) + ) + ) AND + ( + ( + duration<1000 OR + ( + duration BETWEEN 1000 AND 5000 + ) + ) AND + ( + environment!="test" OR + ( + environment="test" AND + is_automated_test=true + ) + ) + ) AND + NOT ( + ( + message CONTAINS "warning" OR + message CONTAINS "deprecated" + ) AND + severity="low" + ) + `, + shouldPass: true, + expectedQuery: "WHERE ((((((((attributes_number['status'] >= ? AND mapContains(attributes_number, 'status') = ?) AND (attributes_number['status'] < ? AND mapContains(attributes_number, 'status') = ?))) OR (((attributes_number['status'] >= ? AND mapContains(attributes_number, 'status') = ?) AND (attributes_number['status'] < ? AND mapContains(attributes_number, 'status') = ?) AND NOT ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?)))))) AND ((((resources_string['service.name'] = ? OR resources_string['service.name'] = ? OR resources_string['service.name'] = ?) AND mapContains(resources_string, 'service.name') = ?) OR (((resources_string['service.type'] = ? AND mapContains(resources_string, 'service.type') = ?) AND NOT ((resources_string['service.deprecated'] = ? AND mapContains(resources_string, 'service.deprecated') = ?)))))))) AND (((((attributes_number['duration'] < ? AND mapContains(attributes_number, 'duration') = ?) OR ((attributes_number['duration'] BETWEEN ? AND ? AND mapContains(attributes_number, 'duration') = ?)))) AND ((resources_string['environment'] <> ? OR (((resources_string['environment'] = ? AND mapContains(resources_string, 'environment') = ?) AND (attributes_bool['is_automated_test'] = ? AND mapContains(attributes_bool, 'is_automated_test') = ?))))))) AND NOT ((((((LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?) OR (LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?))) AND (attributes_string['severity'] = ? AND mapContains(attributes_string, 'severity') = ?)))))", + expectedArgs: []any{ + float64(200), true, float64(300), true, float64(400), true, float64(500), true, float64(404), true, + "api", "web", "auth", true, + "internal", true, true, true, + float64(1000), true, float64(1000), float64(5000), true, + "test", "test", true, true, true, + "%warning%", true, "%deprecated%", true, + "low", true, + }, + }, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("%s: %s", tc.category, limitString(tc.query, 50)), func(t *testing.T) { + + clause, _, err := querybuilder.PrepareWhereClause(tc.query, opts) + + if tc.shouldPass { + if err != nil { + t.Errorf("Failed to parse query: %s\nError: %v\n", tc.query, err) + return + } + + if clause == nil { + t.Errorf("Expected clause for query: %s\n", tc.query) + return + } + + // Build the SQL and print it for debugging + sql, args := clause.BuildWithFlavor(sqlbuilder.ClickHouse) + + require.Equal(t, tc.expectedQuery, sql) + require.Equal(t, tc.expectedArgs, args) + } else { + require.Error(t, err, "Expected error for query: %s", tc.query) + require.Contains(t, err.Error(), tc.expectedErrorContains) + } + }) + } +} diff --git a/pkg/telemetrytests/test_data.go b/pkg/telemetrytests/test_data.go new file mode 100644 index 0000000000..039ed6effb --- /dev/null +++ b/pkg/telemetrytests/test_data.go @@ -0,0 +1,859 @@ +package telemetrytests + +import ( + "strings" + + "github.com/SigNoz/signoz/pkg/types/telemetrytypes" +) + +// Helper function to limit string length for display +func limitString(s string, maxLen int) string { + s = strings.ReplaceAll(s, "\n", " ") + s = strings.ReplaceAll(s, "\t", " ") + + if len(s) <= maxLen { + return s + } + return s[:maxLen-3] + "..." +} + +// Function to build a complete field key map for testing all scenarios +func buildCompleteFieldKeyMap() map[string][]*telemetrytypes.TelemetryFieldKey { + return map[string][]*telemetrytypes.TelemetryFieldKey{ + "service.name": { + { + Name: "service.name", + FieldContext: telemetrytypes.FieldContextResource, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "http.status_code": { + { + Name: "http.status_code", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeInt64, + }, + }, + "status": { + { + Name: "status", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeInt64, + }, + }, + "code": { + { + Name: "code", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeInt64, + }, + }, + "count": { + { + Name: "count", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeInt64, + }, + }, + "duration": { + { + Name: "duration", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeFloat64, + }, + }, + "message": { + { + Name: "message", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "path": { + { + Name: "path", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "email": { + { + Name: "email", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "filename": { + { + Name: "filename", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "amount": { + { + Name: "amount", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeFloat64, + }, + }, + "error.code": { + { + Name: "error.code", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeInt64, + }, + }, + "environment": { + { + Name: "environment", + FieldContext: telemetrytypes.FieldContextResource, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "user.id": { + { + Name: "user.id", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "metadata.version": { + { + Name: "metadata.version", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "request.headers.authorization": { + { + Name: "request.headers.authorization", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "response.body.data": { + { + Name: "response.body.data", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "version": { + { + Name: "version", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "response.headers": { + { + Name: "response.headers", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "request.query_params": { + { + Name: "request.query_params", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "level": { + { + Name: "level", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "user.status": { + { + Name: "user.status", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "isEnabled": { + { + Name: "isEnabled", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeBool, + }, + }, + "isDisabled": { + { + Name: "isDisabled", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeBool, + }, + }, + "is_valid": { + { + Name: "is_valid", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeBool, + }, + }, + "is_invalid": { + { + Name: "is_invalid", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeBool, + }, + }, + "key": { + { + Name: "key", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "status.code": { + { + Name: "status.code", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeInt64, + }, + }, + // Special fields for tests + "value": { + { + Name: "value", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "threshold": { + { + Name: "threshold", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeFloat64, + }, + }, + "warning_threshold": { + { + Name: "warning_threshold", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeFloat64, + }, + }, + "critical_threshold": { + { + Name: "critical_threshold", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeFloat64, + }, + }, + "type": { + { + Name: "type", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "age": { + { + Name: "age", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeInt64, + }, + }, + "user.email": { + { + Name: "user.email", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "user.name": { + { + Name: "user.name", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "user.profile.name": { + { + Name: "user.profile.name", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "region": { + { + Name: "region", + FieldContext: telemetrytypes.FieldContextResource, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "service.type": { + { + Name: "service.type", + FieldContext: telemetrytypes.FieldContextResource, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "service.deprecated": { + { + Name: "service.deprecated", + FieldContext: telemetrytypes.FieldContextResource, + FieldDataType: telemetrytypes.FieldDataTypeBool, + }, + }, + "is_automated_test": { + { + Name: "is_automated_test", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeBool, + }, + }, + "severity": { + { + Name: "severity", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "created_at": { + { + Name: "created_at", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeNumber, + }, + }, + "is_deleted": { + { + Name: "is_deleted", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeBool, + }, + }, + "customer.type": { + { + Name: "customer.type", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "total_orders": { + { + Name: "total_orders", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeInt64, + }, + }, + "total_spent": { + { + Name: "total_spent", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeFloat64, + }, + }, + "items[].product.category": { + { + Name: "items[].product.category", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "items[].license_type": { + { + Name: "items[].license_type", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "first_name": { + { + Name: "first_name", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "last_name": { + { + Name: "last_name", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "address.country": { + { + Name: "address.country", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "address.state": { + { + Name: "address.state", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "address.city": { + { + Name: "address.city", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "subscription.plan": { + { + Name: "subscription.plan", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "subscription.status": { + { + Name: "subscription.status", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "is_expected": { + { + Name: "is_expected", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeBool, + }, + }, + "http.method": { + { + Name: "http.method", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "http.path": { + { + Name: "http.path", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "http.status": { + { + Name: "http.status", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeInt64, + }, + }, + "response.body.error": { + { + Name: "response.body.error", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "metric.name": { + { + Name: "metric.name", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "metric.value": { + { + Name: "metric.value", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeFloat64, + }, + }, + "metric.rate_of_change": { + { + Name: "metric.rate_of_change", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeFloat64, + }, + }, + "resource.type": { + { + Name: "resource.type", + FieldContext: telemetrytypes.FieldContextResource, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "resource.environment": { + { + Name: "resource.environment", + FieldContext: telemetrytypes.FieldContextResource, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "resource.is_critical": { + { + Name: "resource.is_critical", + FieldContext: telemetrytypes.FieldContextResource, + FieldDataType: telemetrytypes.FieldDataTypeBool, + }, + }, + "metric.is_monitored": { + { + Name: "metric.is_monitored", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeBool, + }, + }, + "aggregation.window": { + { + Name: "aggregation.window", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "aggregation.function": { + { + Name: "aggregation.function", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "action": { + { + Name: "action", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "host": { + { + Name: "host", + FieldContext: telemetrytypes.FieldContextResource, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "metric.type": { + { + Name: "metric.type", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "rate": { + { + Name: "rate", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeFloat64, + }, + }, + "delta": { + { + Name: "delta", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeFloat64, + }, + }, + "alerting": { + { + Name: "alerting", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeBool, + }, + }, + "aggregation": { + { + Name: "aggregation", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "user.created_at": { + { + Name: "user.created_at", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeNumber, + }, + }, + "user.type": { + { + Name: "user.type", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "user.last_login": { + { + Name: "user.last_login", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeNumber, + }, + }, + "user.failed_logins": { + { + Name: "user.failed_logins", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeInt64, + }, + }, + "user.department": { + { + Name: "user.department", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "user.region": { + { + Name: "user.region", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "user.quota": { + { + Name: "user.quota", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeInt64, + }, + }, + "users[].role": { + { + Name: "users[].role", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "users[].status": { + { + Name: "users[].status", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "orders[].items[].product.id": { + { + Name: "orders[].items[].product.id", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeInt64, + }, + }, + "data.metrics[].value": { + { + Name: "data.metrics[].value", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeFloat64, + }, + }, + "data.metrics[].name": { + { + Name: "data.metrics[].name", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "requests[].response.status": { + { + Name: "requests[].response.status", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeInt64, + }, + }, + // Unicode characters in keys + "école.name": { + { + Name: "école.name", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "straße.name": { + { + Name: "straße.name", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "日本語.text": { + { + Name: "日本語.text", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "россия.capital": { + { + Name: "россия.capital", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + // Special characters in keys + "special-key": { + { + Name: "special-key", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "special.key": { + { + Name: "special.key", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "special_key": { + { + Name: "special_key", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "special:key": { + { + Name: "special:key", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "key-with-dashes": { + { + Name: "key-with-dashes", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "key_with_underscore": { + { + Name: "key_with_underscore", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + // Nested paths with dots + "and.value": { + { + Name: "and.value", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeBool, + }, + }, + "or.status": { + { + Name: "or.status", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeBool, + }, + }, + "not.enabled": { + { + Name: "not.enabled", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeBool, + }, + }, + "like.pattern": { + { + Name: "like.pattern", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "between.min": { + { + Name: "between.min", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeInt64, + }, + }, + "between.max": { + { + Name: "between.max", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeInt64, + }, + }, + "exists.flag": { + { + Name: "exists.flag", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeBool, + }, + }, + "contains.text": { + { + Name: "contains.text", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "items[0].name": { + { + Name: "items[0].name", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "errors[].code": { + { + Name: "errors[].code", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeInt64, + }, + }, + "metadata.dimensions": { + { + Name: "metadata.dimensions", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "metadata.dimensions.width": { + { + Name: "metadata.dimensions.width", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeInt64, + }, + }, + "subscription_type": { + { + Name: "subscription_type", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + // Resource-related fields from original example + "resource.k8s.namespace.name": { + { + Name: "resource.k8s.namespace.name", + FieldContext: telemetrytypes.FieldContextResource, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + // Add location field for Unicode test + "location": { + { + Name: "location", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + // Add description field for Unicode test + "description": { + { + Name: "description", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + // Add query field for special characters test + "query": { + { + Name: "query", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + }, + "materialized.key.name": { + { + Name: "materialized.key.name", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + Materialized: true, + }, + }, + } +} diff --git a/pkg/telemetrytraces/condition_builder.go b/pkg/telemetrytraces/condition_builder.go index 0f18a3b56e..eacb3d24ae 100644 --- a/pkg/telemetrytraces/condition_builder.go +++ b/pkg/telemetrytraces/condition_builder.go @@ -3,6 +3,7 @@ package telemetrytraces import ( "context" "fmt" + "slices" schema "github.com/SigNoz/signoz-otel-collector/cmd/signozschemamigrator/schema_migrator" qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5" @@ -20,7 +21,7 @@ func NewConditionBuilder(fm qbtypes.FieldMapper) *conditionBuilder { return &conditionBuilder{fm: fm} } -func (c *conditionBuilder) ConditionFor( +func (c *conditionBuilder) conditionFor( ctx context.Context, key *telemetrytypes.TelemetryFieldKey, operator qbtypes.FilterOperator, @@ -73,11 +74,9 @@ func (c *conditionBuilder) ConditionFor( return sb.NotILike(tblFieldName, fmt.Sprintf("%%%s%%", value)), nil case qbtypes.FilterOperatorRegexp: - exp := fmt.Sprintf(`match(%s, %s)`, tblFieldName, sb.Var(value)) - return sb.And(exp), nil + return fmt.Sprintf(`match(%s, %s)`, tblFieldName, sb.Var(value)), nil case qbtypes.FilterOperatorNotRegexp: - exp := fmt.Sprintf(`not match(%s, %s)`, tblFieldName, sb.Var(value)) - return sb.And(exp), nil + return fmt.Sprintf(`NOT match(%s, %s)`, tblFieldName, sb.Var(value)), nil // between and not between case qbtypes.FilterOperatorBetween: @@ -105,13 +104,23 @@ func (c *conditionBuilder) ConditionFor( if !ok { return "", qbtypes.ErrInValues } - return sb.In(tblFieldName, values...), nil + // instead of using IN, we use `=` + `OR` to make use of index + conditions := []string{} + for _, value := range values { + conditions = append(conditions, sb.E(tblFieldName, value)) + } + return sb.Or(conditions...), nil case qbtypes.FilterOperatorNotIn: values, ok := value.([]any) if !ok { return "", qbtypes.ErrInValues } - return sb.NotIn(tblFieldName, values...), nil + // instead of using NOT IN, we use `!=` + `AND` to make use of index + conditions := []string{} + for _, value := range values { + conditions = append(conditions, sb.NE(tblFieldName, value)) + } + return sb.And(conditions...), nil // exists and not exists // in the query builder, `exists` and `not exists` are used for @@ -166,3 +175,31 @@ func (c *conditionBuilder) ConditionFor( } return "", nil } + +func (c *conditionBuilder) ConditionFor( + ctx context.Context, + key *telemetrytypes.TelemetryFieldKey, + operator qbtypes.FilterOperator, + value any, + sb *sqlbuilder.SelectBuilder, +) (string, error) { + condition, err := c.conditionFor(ctx, key, operator, value, sb) + if err != nil { + return "", err + } + + if operator.AddDefaultExistsFilter() { + // skip adding exists filter for intrinsic fields + field, _ := c.fm.FieldFor(ctx, key) + if slices.Contains(IntrinsicFields, field) || slices.Contains(CalculatedFields, field) { + return condition, nil + } + + existsCondition, err := c.conditionFor(ctx, key, qbtypes.FilterOperatorExists, nil, sb) + if err != nil { + return "", err + } + return sb.And(condition, existsCondition), nil + } + return condition, nil +} diff --git a/pkg/telemetrytraces/condition_builder_test.go b/pkg/telemetrytraces/condition_builder_test.go index 1e7f8df264..a990648639 100644 --- a/pkg/telemetrytraces/condition_builder_test.go +++ b/pkg/telemetrytraces/condition_builder_test.go @@ -20,6 +20,7 @@ func TestConditionFor(t *testing.T) { operator qbtypes.FilterOperator value any expectedSQL string + expectedArgs []any expectedError error }{ { @@ -31,6 +32,7 @@ func TestConditionFor(t *testing.T) { operator: qbtypes.FilterOperatorNotEqual, value: uint64(1617979338000000000), expectedSQL: "timestamp <> ?", + expectedArgs: []any{uint64(1617979338000000000)}, expectedError: nil, }, { @@ -42,7 +44,8 @@ func TestConditionFor(t *testing.T) { }, operator: qbtypes.FilterOperatorGreaterThan, value: float64(100), - expectedSQL: "attributes_number['request.duration'] > ?", + expectedSQL: "(attributes_number['request.duration'] > ? AND mapContains(attributes_number, 'request.duration') = ?)", + expectedArgs: []any{float64(100), true}, expectedError: nil, }, { @@ -54,7 +57,8 @@ func TestConditionFor(t *testing.T) { }, operator: qbtypes.FilterOperatorLessThan, value: float64(1024), - expectedSQL: "attributes_number['request.size'] < ?", + expectedSQL: "(attributes_number['request.size'] < ? AND mapContains(attributes_number, 'request.size') = ?)", + expectedArgs: []any{float64(1024), true}, expectedError: nil, }, { @@ -66,6 +70,7 @@ func TestConditionFor(t *testing.T) { operator: qbtypes.FilterOperatorGreaterThanOrEq, value: uint64(1617979338000000000), expectedSQL: "timestamp >= ?", + expectedArgs: []any{uint64(1617979338000000000)}, expectedError: nil, }, { @@ -77,6 +82,7 @@ func TestConditionFor(t *testing.T) { operator: qbtypes.FilterOperatorLessThanOrEq, value: uint64(1617979338000000000), expectedSQL: "timestamp <= ?", + expectedArgs: []any{uint64(1617979338000000000)}, expectedError: nil, }, { @@ -88,7 +94,8 @@ func TestConditionFor(t *testing.T) { }, operator: qbtypes.FilterOperatorILike, value: "%admin%", - expectedSQL: "WHERE LOWER(attributes_string['user.id']) LIKE LOWER(?)", + expectedSQL: "(LOWER(attributes_string['user.id']) LIKE LOWER(?) AND mapContains(attributes_string, 'user.id') = ?)", + expectedArgs: []any{"%admin%", true}, expectedError: nil, }, { @@ -101,6 +108,7 @@ func TestConditionFor(t *testing.T) { operator: qbtypes.FilterOperatorNotILike, value: "%admin%", expectedSQL: "WHERE LOWER(attributes_string['user.id']) NOT LIKE LOWER(?)", + expectedArgs: []any{"%admin%", true}, expectedError: nil, }, { @@ -112,6 +120,7 @@ func TestConditionFor(t *testing.T) { operator: qbtypes.FilterOperatorBetween, value: []any{uint64(1617979338000000000), uint64(1617979348000000000)}, expectedSQL: "timestamp BETWEEN ? AND ?", + expectedArgs: []any{uint64(1617979338000000000), uint64(1617979348000000000)}, expectedError: nil, }, { @@ -145,6 +154,7 @@ func TestConditionFor(t *testing.T) { operator: qbtypes.FilterOperatorNotBetween, value: []any{uint64(1617979338000000000), uint64(1617979348000000000)}, expectedSQL: "timestamp NOT BETWEEN ? AND ?", + expectedArgs: []any{uint64(1617979338000000000), uint64(1617979348000000000)}, expectedError: nil, }, { @@ -180,7 +190,34 @@ func TestConditionFor(t *testing.T) { }, operator: qbtypes.FilterOperatorContains, value: "admin", - expectedSQL: "WHERE LOWER(attributes_string['user.id']) LIKE LOWER(?)", + expectedSQL: "(LOWER(attributes_string['user.id']) LIKE LOWER(?) AND mapContains(attributes_string, 'user.id') = ?)", + expectedArgs: []any{"%admin%", true}, + expectedError: nil, + }, + { + name: "In operator - map field", + key: telemetrytypes.TelemetryFieldKey{ + Name: "user.id", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + operator: qbtypes.FilterOperatorIn, + value: []any{"admin", "user"}, + expectedSQL: "((attributes_string['user.id'] = ? OR attributes_string['user.id'] = ?) AND mapContains(attributes_string, 'user.id') = ?)", + expectedArgs: []any{"admin", "user", true}, + expectedError: nil, + }, + { + name: "Not In operator - map field", + key: telemetrytypes.TelemetryFieldKey{ + Name: "user.id", + FieldContext: telemetrytypes.FieldContextAttribute, + FieldDataType: telemetrytypes.FieldDataTypeString, + }, + operator: qbtypes.FilterOperatorNotIn, + value: []any{"admin", "user"}, + expectedSQL: "(attributes_string['user.id'] <> ? AND attributes_string['user.id'] <> ?)", + expectedArgs: []any{"admin", "user", true}, expectedError: nil, }, { diff --git a/pkg/telemetrytraces/const.go b/pkg/telemetrytraces/const.go new file mode 100644 index 0000000000..45fcdb2965 --- /dev/null +++ b/pkg/telemetrytraces/const.go @@ -0,0 +1,51 @@ +package telemetrytraces + +var ( + IntrinsicFields = []string{ + "timestamp", + "trace_id", + "span_id", + "trace_state", + "parent_span_id", + "flags", + "name", + "kind", + "kind_string", + "duration_nano", + "status_code", + "status_message", + "status_code_string", + "traceID", + "spanID", + "parentSpanID", + "spanKind", + "durationNano", + "statusCode", + "statusMessage", + "statusCodeString", + } + + CalculatedFields = []string{ + "response_status_code", + "external_http_url", + "http_url", + "external_http_method", + "http_method", + "http_host", + "db_name", + "db_operation", + "has_error", + "is_remote", + + "responseStatusCode", + "externalHttpUrl", + "httpUrl", + "externalHttpMethod", + "httpMethod", + "httpHost", + "dbName", + "dbOperation", + "hasError", + "isRemote", + } +) diff --git a/pkg/types/querybuildertypes/querybuildertypesv5/builder_elements.go b/pkg/types/querybuildertypes/querybuildertypesv5/builder_elements.go index 856cd7a15b..9f282bb5dd 100644 --- a/pkg/types/querybuildertypes/querybuildertypesv5/builder_elements.go +++ b/pkg/types/querybuildertypes/querybuildertypesv5/builder_elements.go @@ -82,6 +82,35 @@ const ( FilterOperatorNotContains ) +// AddDefaultExistsFilter returns true if addl exists filter should be added to the query +// For the negative predicates, we don't want to add the exists filter. Why? +// Say for example, user adds a filter `service.name != "redis"`, we can't interpret it +// unambiguously i.e do they mean to fetch logs that satisfy +// service.name != "redis" or do they mean to fetch logs that have `service.name` field and +// doesn't have value "redis" +// Since we don't know the intent, we don't add the exists filter. They are expected +// to add exists filter themselves if exclusion is desired. +// +// For the positive predicates, the key existence is implied. +func (f FilterOperator) AddDefaultExistsFilter() bool { + switch f { + case + FilterOperatorEqual, + FilterOperatorGreaterThan, + FilterOperatorGreaterThanOrEq, + FilterOperatorLessThan, + FilterOperatorLessThanOrEq, + FilterOperatorLike, + FilterOperatorILike, + FilterOperatorBetween, + FilterOperatorIn, + FilterOperatorRegexp, + FilterOperatorContains: + return true + } + return false +} + type OrderDirection struct { valuer.String } diff --git a/pkg/types/telemetrytypes/field.go b/pkg/types/telemetrytypes/field.go index db31a510d0..cb8a08883c 100644 --- a/pkg/types/telemetrytypes/field.go +++ b/pkg/types/telemetrytypes/field.go @@ -99,11 +99,11 @@ func GetFieldKeyFromKeyText(key string) TelemetryFieldKey { } func FieldKeyToMaterializedColumnName(key *TelemetryFieldKey) string { - return fmt.Sprintf("%s_%s_%s", key.FieldContext.String, fieldDataTypes[key.FieldDataType.StringValue()].StringValue(), strings.ReplaceAll(key.Name, ".", "$$")) + return fmt.Sprintf("`%s_%s_%s`", key.FieldContext.String, fieldDataTypes[key.FieldDataType.StringValue()].StringValue(), strings.ReplaceAll(key.Name, ".", "$$")) } func FieldKeyToMaterializedColumnNameForExists(key *TelemetryFieldKey) string { - return fmt.Sprintf("%s_%s_%s_exists", key.FieldContext.String, fieldDataTypes[key.FieldDataType.StringValue()].StringValue(), strings.ReplaceAll(key.Name, ".", "$$")) + return fmt.Sprintf("`%s_%s_%s_exists`", key.FieldContext.String, fieldDataTypes[key.FieldDataType.StringValue()].StringValue(), strings.ReplaceAll(key.Name, ".", "$$")) } type TelemetryFieldValues struct {