[ES|QL] Update grammars (#217277)

This PR updates the ES|QL grammars (lexer and parser) to match the
latest version in Elasticsearch.

---------

Co-authored-by: Stratoula Kalafateli <efstratia.kalafateli@elastic.co>
Co-authored-by: Vadim Kibana <82822460+vadimkibana@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2025-04-07 15:25:32 +02:00 committed by GitHub
parent 4f97072ffd
commit 338aa07e08
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 2283 additions and 2132 deletions

View file

@ -11,7 +11,7 @@ export type {
ESQLAst,
ESQLAstItem,
ESQLAstCommand,
ESQLAstMetricsCommand,
ESQLAstTimeseriesCommand,
ESQLAstJoinCommand,
ESQLCommand,
ESQLCommandOption,

File diff suppressed because one or more lines are too long

View file

@ -13,49 +13,49 @@ SORT=12
STATS=13
WHERE=14
DEV_INLINESTATS=15
FROM=16
DEV_METRICS=17
DEV_FORK=18
JOIN_LOOKUP=19
DEV_JOIN_FULL=20
DEV_JOIN_LEFT=21
DEV_JOIN_RIGHT=22
DEV_LOOKUP=23
MV_EXPAND=24
DROP=25
KEEP=26
DEV_INSIST=27
DEV_RRF=28
RENAME=29
SHOW=30
UNKNOWN_CMD=31
CHANGE_POINT_LINE_COMMENT=32
CHANGE_POINT_MULTILINE_COMMENT=33
CHANGE_POINT_WS=34
ON=35
WITH=36
ENRICH_POLICY_NAME=37
ENRICH_LINE_COMMENT=38
ENRICH_MULTILINE_COMMENT=39
ENRICH_WS=40
ENRICH_FIELD_LINE_COMMENT=41
ENRICH_FIELD_MULTILINE_COMMENT=42
ENRICH_FIELD_WS=43
SETTING=44
SETTING_LINE_COMMENT=45
SETTTING_MULTILINE_COMMENT=46
SETTING_WS=47
EXPLAIN_WS=48
EXPLAIN_LINE_COMMENT=49
EXPLAIN_MULTILINE_COMMENT=50
PIPE=51
QUOTED_STRING=52
INTEGER_LITERAL=53
DECIMAL_LITERAL=54
BY=55
AND=56
ASC=57
ASSIGN=58
DEV_RERANK=16
FROM=17
DEV_TIME_SERIES=18
DEV_FORK=19
JOIN_LOOKUP=20
DEV_JOIN_FULL=21
DEV_JOIN_LEFT=22
DEV_JOIN_RIGHT=23
DEV_LOOKUP=24
MV_EXPAND=25
DROP=26
KEEP=27
DEV_INSIST=28
DEV_RRF=29
RENAME=30
SHOW=31
UNKNOWN_CMD=32
CHANGE_POINT_LINE_COMMENT=33
CHANGE_POINT_MULTILINE_COMMENT=34
CHANGE_POINT_WS=35
ENRICH_POLICY_NAME=36
ENRICH_LINE_COMMENT=37
ENRICH_MULTILINE_COMMENT=38
ENRICH_WS=39
ENRICH_FIELD_LINE_COMMENT=40
ENRICH_FIELD_MULTILINE_COMMENT=41
ENRICH_FIELD_WS=42
SETTING=43
SETTING_LINE_COMMENT=44
SETTTING_MULTILINE_COMMENT=45
SETTING_WS=46
EXPLAIN_WS=47
EXPLAIN_LINE_COMMENT=48
EXPLAIN_MULTILINE_COMMENT=49
PIPE=50
QUOTED_STRING=51
INTEGER_LITERAL=52
DECIMAL_LITERAL=53
AND=54
AS=55
ASC=56
ASSIGN=57
BY=58
CAST_OP=59
COLON=60
COMMA=61
@ -70,70 +70,71 @@ LIKE=69
NOT=70
NULL=71
NULLS=72
OR=73
PARAM=74
RLIKE=75
TRUE=76
EQ=77
CIEQ=78
NEQ=79
LT=80
LTE=81
GT=82
GTE=83
PLUS=84
MINUS=85
ASTERISK=86
SLASH=87
PERCENT=88
LEFT_BRACES=89
RIGHT_BRACES=90
DOUBLE_PARAMS=91
NAMED_OR_POSITIONAL_PARAM=92
NAMED_OR_POSITIONAL_DOUBLE_PARAMS=93
OPENING_BRACKET=94
CLOSING_BRACKET=95
LP=96
RP=97
UNQUOTED_IDENTIFIER=98
QUOTED_IDENTIFIER=99
EXPR_LINE_COMMENT=100
EXPR_MULTILINE_COMMENT=101
EXPR_WS=102
METADATA=103
UNQUOTED_SOURCE=104
FROM_LINE_COMMENT=105
FROM_MULTILINE_COMMENT=106
FROM_WS=107
FORK_WS=108
FORK_LINE_COMMENT=109
FORK_MULTILINE_COMMENT=110
JOIN=111
USING=112
JOIN_LINE_COMMENT=113
JOIN_MULTILINE_COMMENT=114
JOIN_WS=115
LOOKUP_LINE_COMMENT=116
LOOKUP_MULTILINE_COMMENT=117
LOOKUP_WS=118
LOOKUP_FIELD_LINE_COMMENT=119
LOOKUP_FIELD_MULTILINE_COMMENT=120
LOOKUP_FIELD_WS=121
MVEXPAND_LINE_COMMENT=122
MVEXPAND_MULTILINE_COMMENT=123
MVEXPAND_WS=124
ID_PATTERN=125
PROJECT_LINE_COMMENT=126
PROJECT_MULTILINE_COMMENT=127
PROJECT_WS=128
AS=129
RENAME_LINE_COMMENT=130
RENAME_MULTILINE_COMMENT=131
RENAME_WS=132
INFO=133
SHOW_LINE_COMMENT=134
SHOW_MULTILINE_COMMENT=135
SHOW_WS=136
ON=73
OR=74
PARAM=75
RLIKE=76
TRUE=77
WITH=78
EQ=79
CIEQ=80
NEQ=81
LT=82
LTE=83
GT=84
GTE=85
PLUS=86
MINUS=87
ASTERISK=88
SLASH=89
PERCENT=90
LEFT_BRACES=91
RIGHT_BRACES=92
DOUBLE_PARAMS=93
NAMED_OR_POSITIONAL_PARAM=94
NAMED_OR_POSITIONAL_DOUBLE_PARAMS=95
OPENING_BRACKET=96
CLOSING_BRACKET=97
LP=98
RP=99
UNQUOTED_IDENTIFIER=100
QUOTED_IDENTIFIER=101
EXPR_LINE_COMMENT=102
EXPR_MULTILINE_COMMENT=103
EXPR_WS=104
METADATA=105
UNQUOTED_SOURCE=106
FROM_LINE_COMMENT=107
FROM_MULTILINE_COMMENT=108
FROM_WS=109
FORK_WS=110
FORK_LINE_COMMENT=111
FORK_MULTILINE_COMMENT=112
JOIN=113
USING=114
JOIN_LINE_COMMENT=115
JOIN_MULTILINE_COMMENT=116
JOIN_WS=117
LOOKUP_LINE_COMMENT=118
LOOKUP_MULTILINE_COMMENT=119
LOOKUP_WS=120
LOOKUP_FIELD_LINE_COMMENT=121
LOOKUP_FIELD_MULTILINE_COMMENT=122
LOOKUP_FIELD_WS=123
MVEXPAND_LINE_COMMENT=124
MVEXPAND_MULTILINE_COMMENT=125
MVEXPAND_WS=126
ID_PATTERN=127
PROJECT_LINE_COMMENT=128
PROJECT_MULTILINE_COMMENT=129
PROJECT_WS=130
RENAME_LINE_COMMENT=131
RENAME_MULTILINE_COMMENT=132
RENAME_WS=133
INFO=134
SHOW_LINE_COMMENT=135
SHOW_MULTILINE_COMMENT=136
SHOW_WS=137
'enrich'=5
'explain'=6
'dissect'=7
@ -144,20 +145,19 @@ SHOW_WS=136
'sort'=12
'stats'=13
'where'=14
'from'=16
'lookup'=19
'mv_expand'=24
'drop'=25
'keep'=26
'rename'=29
'show'=30
'on'=35
'with'=36
'|'=51
'by'=55
'and'=56
'asc'=57
'='=58
'from'=17
'lookup'=20
'mv_expand'=25
'drop'=26
'keep'=27
'rename'=30
'show'=31
'|'=50
'and'=54
'as'=55
'asc'=56
'='=57
'by'=58
'::'=59
':'=60
','=61
@ -172,29 +172,30 @@ SHOW_WS=136
'not'=70
'null'=71
'nulls'=72
'or'=73
'?'=74
'rlike'=75
'true'=76
'=='=77
'=~'=78
'!='=79
'<'=80
'<='=81
'>'=82
'>='=83
'+'=84
'-'=85
'*'=86
'/'=87
'%'=88
'{'=89
'}'=90
'??'=91
']'=95
')'=97
'metadata'=103
'join'=111
'USING'=112
'as'=129
'info'=133
'on'=73
'or'=74
'?'=75
'rlike'=76
'true'=77
'with'=78
'=='=79
'=~'=80
'!='=81
'<'=82
'<='=83
'>'=84
'>='=85
'+'=86
'-'=87
'*'=88
'/'=89
'%'=90
'{'=91
'}'=92
'??'=93
']'=97
')'=99
'metadata'=105
'join'=113
'USING'=114
'info'=134

View file

@ -40,7 +40,7 @@ sourceCommand
| rowCommand
| showCommand
// in development
| {this.isDevVersion()}? metricsCommand
| {this.isDevVersion()}? timeSeriesCommand
;
processingCommand
@ -63,6 +63,7 @@ processingCommand
| {this.isDevVersion()}? changePointCommand
| {this.isDevVersion()}? insistCommand
| {this.isDevVersion()}? forkCommand
| {this.isDevVersion()}? rerankCommand
| {this.isDevVersion()}? rrfCommand
;
@ -90,8 +91,8 @@ fromCommand
: FROM indexPatternAndMetadataFields
;
metricsCommand
: DEV_METRICS indexPatternAndMetadataFields
timeSeriesCommand
: DEV_TIME_SERIES indexPatternAndMetadataFields
;
indexPatternAndMetadataFields:
@ -289,4 +290,8 @@ forkSubQueryProcessingCommand
rrfCommand
: DEV_RRF
;
;
rerankCommand
: DEV_RERANK queryText=constant ON fields WITH inferenceId=identifierOrParameter
;

File diff suppressed because one or more lines are too long

View file

@ -13,49 +13,49 @@ SORT=12
STATS=13
WHERE=14
DEV_INLINESTATS=15
FROM=16
DEV_METRICS=17
DEV_FORK=18
JOIN_LOOKUP=19
DEV_JOIN_FULL=20
DEV_JOIN_LEFT=21
DEV_JOIN_RIGHT=22
DEV_LOOKUP=23
MV_EXPAND=24
DROP=25
KEEP=26
DEV_INSIST=27
DEV_RRF=28
RENAME=29
SHOW=30
UNKNOWN_CMD=31
CHANGE_POINT_LINE_COMMENT=32
CHANGE_POINT_MULTILINE_COMMENT=33
CHANGE_POINT_WS=34
ON=35
WITH=36
ENRICH_POLICY_NAME=37
ENRICH_LINE_COMMENT=38
ENRICH_MULTILINE_COMMENT=39
ENRICH_WS=40
ENRICH_FIELD_LINE_COMMENT=41
ENRICH_FIELD_MULTILINE_COMMENT=42
ENRICH_FIELD_WS=43
SETTING=44
SETTING_LINE_COMMENT=45
SETTTING_MULTILINE_COMMENT=46
SETTING_WS=47
EXPLAIN_WS=48
EXPLAIN_LINE_COMMENT=49
EXPLAIN_MULTILINE_COMMENT=50
PIPE=51
QUOTED_STRING=52
INTEGER_LITERAL=53
DECIMAL_LITERAL=54
BY=55
AND=56
ASC=57
ASSIGN=58
DEV_RERANK=16
FROM=17
DEV_TIME_SERIES=18
DEV_FORK=19
JOIN_LOOKUP=20
DEV_JOIN_FULL=21
DEV_JOIN_LEFT=22
DEV_JOIN_RIGHT=23
DEV_LOOKUP=24
MV_EXPAND=25
DROP=26
KEEP=27
DEV_INSIST=28
DEV_RRF=29
RENAME=30
SHOW=31
UNKNOWN_CMD=32
CHANGE_POINT_LINE_COMMENT=33
CHANGE_POINT_MULTILINE_COMMENT=34
CHANGE_POINT_WS=35
ENRICH_POLICY_NAME=36
ENRICH_LINE_COMMENT=37
ENRICH_MULTILINE_COMMENT=38
ENRICH_WS=39
ENRICH_FIELD_LINE_COMMENT=40
ENRICH_FIELD_MULTILINE_COMMENT=41
ENRICH_FIELD_WS=42
SETTING=43
SETTING_LINE_COMMENT=44
SETTTING_MULTILINE_COMMENT=45
SETTING_WS=46
EXPLAIN_WS=47
EXPLAIN_LINE_COMMENT=48
EXPLAIN_MULTILINE_COMMENT=49
PIPE=50
QUOTED_STRING=51
INTEGER_LITERAL=52
DECIMAL_LITERAL=53
AND=54
AS=55
ASC=56
ASSIGN=57
BY=58
CAST_OP=59
COLON=60
COMMA=61
@ -70,70 +70,71 @@ LIKE=69
NOT=70
NULL=71
NULLS=72
OR=73
PARAM=74
RLIKE=75
TRUE=76
EQ=77
CIEQ=78
NEQ=79
LT=80
LTE=81
GT=82
GTE=83
PLUS=84
MINUS=85
ASTERISK=86
SLASH=87
PERCENT=88
LEFT_BRACES=89
RIGHT_BRACES=90
DOUBLE_PARAMS=91
NAMED_OR_POSITIONAL_PARAM=92
NAMED_OR_POSITIONAL_DOUBLE_PARAMS=93
OPENING_BRACKET=94
CLOSING_BRACKET=95
LP=96
RP=97
UNQUOTED_IDENTIFIER=98
QUOTED_IDENTIFIER=99
EXPR_LINE_COMMENT=100
EXPR_MULTILINE_COMMENT=101
EXPR_WS=102
METADATA=103
UNQUOTED_SOURCE=104
FROM_LINE_COMMENT=105
FROM_MULTILINE_COMMENT=106
FROM_WS=107
FORK_WS=108
FORK_LINE_COMMENT=109
FORK_MULTILINE_COMMENT=110
JOIN=111
USING=112
JOIN_LINE_COMMENT=113
JOIN_MULTILINE_COMMENT=114
JOIN_WS=115
LOOKUP_LINE_COMMENT=116
LOOKUP_MULTILINE_COMMENT=117
LOOKUP_WS=118
LOOKUP_FIELD_LINE_COMMENT=119
LOOKUP_FIELD_MULTILINE_COMMENT=120
LOOKUP_FIELD_WS=121
MVEXPAND_LINE_COMMENT=122
MVEXPAND_MULTILINE_COMMENT=123
MVEXPAND_WS=124
ID_PATTERN=125
PROJECT_LINE_COMMENT=126
PROJECT_MULTILINE_COMMENT=127
PROJECT_WS=128
AS=129
RENAME_LINE_COMMENT=130
RENAME_MULTILINE_COMMENT=131
RENAME_WS=132
INFO=133
SHOW_LINE_COMMENT=134
SHOW_MULTILINE_COMMENT=135
SHOW_WS=136
ON=73
OR=74
PARAM=75
RLIKE=76
TRUE=77
WITH=78
EQ=79
CIEQ=80
NEQ=81
LT=82
LTE=83
GT=84
GTE=85
PLUS=86
MINUS=87
ASTERISK=88
SLASH=89
PERCENT=90
LEFT_BRACES=91
RIGHT_BRACES=92
DOUBLE_PARAMS=93
NAMED_OR_POSITIONAL_PARAM=94
NAMED_OR_POSITIONAL_DOUBLE_PARAMS=95
OPENING_BRACKET=96
CLOSING_BRACKET=97
LP=98
RP=99
UNQUOTED_IDENTIFIER=100
QUOTED_IDENTIFIER=101
EXPR_LINE_COMMENT=102
EXPR_MULTILINE_COMMENT=103
EXPR_WS=104
METADATA=105
UNQUOTED_SOURCE=106
FROM_LINE_COMMENT=107
FROM_MULTILINE_COMMENT=108
FROM_WS=109
FORK_WS=110
FORK_LINE_COMMENT=111
FORK_MULTILINE_COMMENT=112
JOIN=113
USING=114
JOIN_LINE_COMMENT=115
JOIN_MULTILINE_COMMENT=116
JOIN_WS=117
LOOKUP_LINE_COMMENT=118
LOOKUP_MULTILINE_COMMENT=119
LOOKUP_WS=120
LOOKUP_FIELD_LINE_COMMENT=121
LOOKUP_FIELD_MULTILINE_COMMENT=122
LOOKUP_FIELD_WS=123
MVEXPAND_LINE_COMMENT=124
MVEXPAND_MULTILINE_COMMENT=125
MVEXPAND_WS=126
ID_PATTERN=127
PROJECT_LINE_COMMENT=128
PROJECT_MULTILINE_COMMENT=129
PROJECT_WS=130
RENAME_LINE_COMMENT=131
RENAME_MULTILINE_COMMENT=132
RENAME_WS=133
INFO=134
SHOW_LINE_COMMENT=135
SHOW_MULTILINE_COMMENT=136
SHOW_WS=137
'enrich'=5
'explain'=6
'dissect'=7
@ -144,20 +145,19 @@ SHOW_WS=136
'sort'=12
'stats'=13
'where'=14
'from'=16
'lookup'=19
'mv_expand'=24
'drop'=25
'keep'=26
'rename'=29
'show'=30
'on'=35
'with'=36
'|'=51
'by'=55
'and'=56
'asc'=57
'='=58
'from'=17
'lookup'=20
'mv_expand'=25
'drop'=26
'keep'=27
'rename'=30
'show'=31
'|'=50
'and'=54
'as'=55
'asc'=56
'='=57
'by'=58
'::'=59
':'=60
','=61
@ -172,29 +172,30 @@ SHOW_WS=136
'not'=70
'null'=71
'nulls'=72
'or'=73
'?'=74
'rlike'=75
'true'=76
'=='=77
'=~'=78
'!='=79
'<'=80
'<='=81
'>'=82
'>='=83
'+'=84
'-'=85
'*'=86
'/'=87
'%'=88
'{'=89
'}'=90
'??'=91
']'=95
')'=97
'metadata'=103
'join'=111
'USING'=112
'as'=129
'info'=133
'on'=73
'or'=74
'?'=75
'rlike'=76
'true'=77
'with'=78
'=='=79
'=~'=80
'!='=81
'<'=82
'<='=83
'>'=84
'>='=85
'+'=86
'-'=87
'*'=88
'/'=89
'%'=90
'{'=91
'}'=92
'??'=93
']'=97
')'=99
'metadata'=105
'join'=113
'USING'=114
'info'=134

View file

@ -22,7 +22,7 @@ import { RowCommandContext } from "./esql_parser.js";
import { FieldsContext } from "./esql_parser.js";
import { FieldContext } from "./esql_parser.js";
import { FromCommandContext } from "./esql_parser.js";
import { MetricsCommandContext } from "./esql_parser.js";
import { TimeSeriesCommandContext } from "./esql_parser.js";
import { IndexPatternAndMetadataFieldsContext } from "./esql_parser.js";
import { IndexPatternContext } from "./esql_parser.js";
import { ClusterStringContext } from "./esql_parser.js";
@ -71,6 +71,7 @@ import { SingleForkSubQueryCommandContext } from "./esql_parser.js";
import { CompositeForkSubQueryContext } from "./esql_parser.js";
import { ForkSubQueryProcessingCommandContext } from "./esql_parser.js";
import { RrfCommandContext } from "./esql_parser.js";
import { RerankCommandContext } from "./esql_parser.js";
import { MatchExpressionContext } from "./esql_parser.js";
import { LogicalNotContext } from "./esql_parser.js";
import { BooleanDefaultContext } from "./esql_parser.js";
@ -238,15 +239,15 @@ export default class esql_parserListener extends ParseTreeListener {
*/
exitFromCommand?: (ctx: FromCommandContext) => void;
/**
* Enter a parse tree produced by `esql_parser.metricsCommand`.
* Enter a parse tree produced by `esql_parser.timeSeriesCommand`.
* @param ctx the parse tree
*/
enterMetricsCommand?: (ctx: MetricsCommandContext) => void;
enterTimeSeriesCommand?: (ctx: TimeSeriesCommandContext) => void;
/**
* Exit a parse tree produced by `esql_parser.metricsCommand`.
* Exit a parse tree produced by `esql_parser.timeSeriesCommand`.
* @param ctx the parse tree
*/
exitMetricsCommand?: (ctx: MetricsCommandContext) => void;
exitTimeSeriesCommand?: (ctx: TimeSeriesCommandContext) => void;
/**
* Enter a parse tree produced by `esql_parser.indexPatternAndMetadataFields`.
* @param ctx the parse tree
@ -741,6 +742,16 @@ export default class esql_parserListener extends ParseTreeListener {
* @param ctx the parse tree
*/
exitRrfCommand?: (ctx: RrfCommandContext) => void;
/**
* Enter a parse tree produced by `esql_parser.rerankCommand`.
* @param ctx the parse tree
*/
enterRerankCommand?: (ctx: RerankCommandContext) => void;
/**
* Exit a parse tree produced by `esql_parser.rerankCommand`.
* @param ctx the parse tree
*/
exitRerankCommand?: (ctx: RerankCommandContext) => void;
/**
* Enter a parse tree produced by the `matchExpression`
* labeled alternative in `esql_parser.booleanExpression`.

View file

@ -16,8 +16,8 @@ mode ENRICH_MODE;
ENRICH_PIPE : PIPE -> type(PIPE), popMode;
ENRICH_OPENING_BRACKET : OPENING_BRACKET -> type(OPENING_BRACKET), pushMode(SETTING_MODE);
ON : 'on' -> pushMode(ENRICH_FIELD_MODE);
WITH : 'with' -> pushMode(ENRICH_FIELD_MODE);
ENRICH_ON : ON -> type(ON), pushMode(ENRICH_FIELD_MODE);
ENRICH_WITH : WITH -> type(WITH), pushMode(ENRICH_FIELD_MODE);
// similar to that of an index
// see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html#indices-create-api-path-params

View file

@ -19,6 +19,7 @@ STATS : 'stats' -> pushMode(EXPRESSION_MODE);
WHERE : 'where' -> pushMode(EXPRESSION_MODE);
DEV_INLINESTATS : {this.isDevVersion()}? 'inlinestats' -> pushMode(EXPRESSION_MODE);
DEV_RERANK : {this.isDevVersion()}? 'rerank' -> pushMode(EXPRESSION_MODE);
mode EXPRESSION_MODE;
@ -82,11 +83,12 @@ DECIMAL_LITERAL
| DOT DIGIT+ EXPONENT
;
BY : 'by';
AND : 'and';
AS: 'as';
ASC : 'asc';
ASSIGN : '=';
BY : 'by';
CAST_OP : '::';
COLON : ':';
COMMA : ',';
@ -101,10 +103,12 @@ LIKE: 'like';
NOT : 'not';
NULL : 'null';
NULLS : 'nulls';
ON: 'on';
OR : 'or';
PARAM: '?';
RLIKE: 'rlike';
TRUE : 'true';
WITH: 'with';
EQ : '==';
CIEQ : '=~';

View file

@ -11,7 +11,7 @@ lexer grammar From;
//
FROM : 'from' -> pushMode(FROM_MODE);
DEV_METRICS : {this.isDevVersion()}? 'metrics' -> pushMode(FROM_MODE);
DEV_TIME_SERIES : {this.isDevVersion()}? 'ts' -> pushMode(FROM_MODE);
mode FROM_MODE;
FROM_PIPE : PIPE -> type(PIPE), popMode;

View file

@ -22,7 +22,7 @@ RENAME_NAMED_OR_POSITIONAL_PARAM : NAMED_OR_POSITIONAL_PARAM -> type(NAMED_OR_PO
RENAME_DOUBLE_PARAMS : DOUBLE_PARAMS -> type(DOUBLE_PARAMS);
RENAME_NAMED_OR_POSITIONAL_DOUBLE_PARAMS : NAMED_OR_POSITIONAL_DOUBLE_PARAMS -> type(NAMED_OR_POSITIONAL_DOUBLE_PARAMS);
AS : 'as';
RENAME_AS : AS -> type(AS);
RENAME_ID_PATTERN
: ID_PATTERN -> type(ID_PATTERN)

View file

@ -9,17 +9,17 @@
import { parse } from '..';
describe('METRICS', () => {
describe('Timeseries', () => {
describe('correctly formatted', () => {
it('can parse a basic query', () => {
const text = 'METRICS foo';
const text = 'TS foo';
const { ast, errors } = parse(text);
expect(errors.length).toBe(0);
expect(ast).toMatchObject([
{
type: 'command',
name: 'metrics',
name: 'ts',
sources: [
{
type: 'source',
@ -32,14 +32,14 @@ describe('METRICS', () => {
});
it('can parse multiple "sources"', () => {
const text = 'METRICS foo ,\nbar\t,\t\nbaz \n';
const text = 'TS foo ,\nbar\t,\t\nbaz \n';
const { ast, errors } = parse(text);
expect(errors.length).toBe(0);
expect(ast).toMatchObject([
{
type: 'command',
name: 'metrics',
name: 'ts',
sources: [
{
type: 'source',
@ -64,14 +64,14 @@ describe('METRICS', () => {
describe('when incorrectly formatted, returns errors', () => {
it('when no index identifier specified', () => {
const text = 'METRICS \n\t';
const text = 'TS \n\t';
const { errors } = parse(text);
expect(errors.length > 0).toBe(true);
});
it('when comma follows index identifier', () => {
const text = 'METRICS foo, ';
const text = 'TS foo, ';
const { errors } = parse(text);
expect(errors.length > 0).toBe(true);

View file

@ -27,7 +27,7 @@ import {
type EnrichCommandContext,
type WhereCommandContext,
default as esql_parser,
type MetricsCommandContext,
type TimeSeriesCommandContext,
IndexPatternContext,
InlinestatsCommandContext,
JoinCommandContext,
@ -56,7 +56,7 @@ import {
getMatchField,
getEnrichClauses,
} from './walkers';
import type { ESQLAst, ESQLAstMetricsCommand } from '../types';
import type { ESQLAst, ESQLAstTimeseriesCommand } from '../types';
import { createJoinCommand } from './factories/join';
import { createDissectCommand } from './factories/dissect';
import { createGrokCommand } from './factories/grok';
@ -138,12 +138,12 @@ export class ESQLAstBuilderListener implements ESQLParserListener {
}
/**
* Exit a parse tree produced by `esql_parser.metricsCommand`.
* Exit a parse tree produced by `esql_parser.timeseriesCommand`.
* @param ctx the parse tree
*/
exitMetricsCommand(ctx: MetricsCommandContext): void {
const node: ESQLAstMetricsCommand = {
...createAstBaseItem('metrics', ctx),
exitTimeSeriesCommand(ctx: TimeSeriesCommandContext): void {
const node: ESQLAstTimeseriesCommand = {
...createAstBaseItem('ts', ctx),
type: 'command',
args: [],
sources: ctx

View file

@ -9,7 +9,7 @@
export type ESQLAst = ESQLAstCommand[];
export type ESQLAstCommand = ESQLCommand | ESQLAstMetricsCommand | ESQLAstJoinCommand;
export type ESQLAstCommand = ESQLCommand | ESQLAstTimeseriesCommand | ESQLAstJoinCommand;
export type ESQLAstNode = ESQLAstCommand | ESQLAstExpression | ESQLAstItem;
@ -88,7 +88,7 @@ export interface ESQLCommand<Name = string> extends ESQLAstBaseItem<Name> {
args: ESQLAstItem[];
}
export interface ESQLAstMetricsCommand extends ESQLCommand<'metrics'> {
export interface ESQLAstTimeseriesCommand extends ESQLCommand<'ts'> {
sources: ESQLSource[];
}

View file

@ -150,7 +150,7 @@ You can specify a specific callback for each command, instead of the generic
- `visitLimitCommand` &mdash; Called for every `LIMIT` command node.
- `visitExplainCommand` &mdash; Called for every `EXPLAIN` command node.
- `visitRowCommand` &mdash; Called for every `ROW` command node.
- `visitMetricsCommand` &mdash; Called for every `METRICS` command node.
- `visitTimeseriesCommand` &mdash; Called for every `TS` command node.
- `visitShowCommand` &mdash; Called for every `SHOW` command node.
- `visitMetaCommand` &mdash; Called for every `META` command node.
- `visitEvalCommand` &mdash; Called for every `EVAL` command node.

View file

@ -376,8 +376,8 @@ export class RowCommandVisitorContext<
Data extends SharedData = SharedData
> extends CommandVisitorContext<Methods, Data, ESQLAstCommand> {}
// METRICS
export class MetricsCommandVisitorContext<
// TS
export class TimeseriesCommandVisitorContext<
Methods extends VisitorMethods = VisitorMethods,
Data extends SharedData = SharedData
> extends CommandVisitorContext<Methods, Data, ESQLAstCommand> {}

View file

@ -104,9 +104,9 @@ export class GlobalVisitorContext<
return this.visitRowCommand(parent, commandNode, input as any);
}
// TODO: uncomment this when the command is implemented
// case 'metrics': {
// if (!this.methods.visitMetricsCommand) break;
// return this.visitMetricsCommand(parent, commandNode, input as any);
// case 'ts': {
// if (!this.methods.visitTimeseriesCommand) break;
// return this.visitTimeseriesCommand(parent, commandNode, input as any);
// }
case 'show': {
if (!this.methods.visitShowCommand) break;
@ -220,13 +220,13 @@ export class GlobalVisitorContext<
return this.visitWithSpecificContext('visitRowCommand', context, input);
}
public visitMetricsCommand(
public visitTimeseriesCommand(
parent: contexts.VisitorContext | null,
node: ESQLAstCommand,
input: types.VisitorInput<Methods, 'visitMetricsCommand'>
): types.VisitorOutput<Methods, 'visitMetricsCommand'> {
const context = new contexts.MetricsCommandVisitorContext(this, node, parent);
return this.visitWithSpecificContext('visitMetricsCommand', context, input);
input: types.VisitorInput<Methods, 'visitTimeseriesCommand'>
): types.VisitorOutput<Methods, 'visitTimeseriesCommand'> {
const context = new contexts.TimeseriesCommandVisitorContext(this, node, parent);
return this.visitWithSpecificContext('visitTimeseriesCommand', context, input);
}
public visitShowCommand(

View file

@ -90,7 +90,7 @@ export type CommandVisitorInput<Methods extends VisitorMethods> = AnyToVoid<
VisitorInput<Methods, 'visitLimitCommand'> &
VisitorInput<Methods, 'visitExplainCommand'> &
VisitorInput<Methods, 'visitRowCommand'> &
VisitorInput<Methods, 'visitMetricsCommand'> &
VisitorInput<Methods, 'visitTimeseriesCommand'> &
VisitorInput<Methods, 'visitShowCommand'> &
VisitorInput<Methods, 'visitMetaCommand'> &
VisitorInput<Methods, 'visitEvalCommand'> &
@ -119,7 +119,7 @@ export type CommandVisitorOutput<Methods extends VisitorMethods> =
| VisitorOutput<Methods, 'visitLimitCommand'>
| VisitorOutput<Methods, 'visitExplainCommand'>
| VisitorOutput<Methods, 'visitRowCommand'>
| VisitorOutput<Methods, 'visitMetricsCommand'>
| VisitorOutput<Methods, 'visitTimeseriesCommand'>
| VisitorOutput<Methods, 'visitShowCommand'>
| VisitorOutput<Methods, 'visitMetaCommand'>
| VisitorOutput<Methods, 'visitEvalCommand'>
@ -148,7 +148,11 @@ export interface VisitorMethods<
visitLimitCommand?: Visitor<contexts.LimitCommandVisitorContext<Visitors, Data>, any, any>;
visitExplainCommand?: Visitor<contexts.ExplainCommandVisitorContext<Visitors, Data>, any, any>;
visitRowCommand?: Visitor<contexts.RowCommandVisitorContext<Visitors, Data>, any, any>;
visitMetricsCommand?: Visitor<contexts.MetricsCommandVisitorContext<Visitors, Data>, any, any>;
visitTimeseriesCommand?: Visitor<
contexts.TimeseriesCommandVisitorContext<Visitors, Data>,
any,
any
>;
visitShowCommand?: Visitor<contexts.ShowCommandVisitorContext<Visitors, Data>, any, any>;
visitMetaCommand?: Visitor<contexts.MetaCommandVisitorContext<Visitors, Data>, any, any>;
visitEvalCommand?: Visitor<contexts.EvalCommandVisitorContext<Visitors, Data>, any, any>;

View file

@ -25,7 +25,7 @@ import {
import { walk, Walker } from './walker';
test('can walk all functions', () => {
const { root } = parse('METRICS index | EVAL a(b(c(foo)))');
const { root } = parse('TS index | EVAL a(b(c(foo)))');
const functions: string[] = [];
walk(root, {
@ -36,7 +36,7 @@ test('can walk all functions', () => {
});
test('can find assignment expression', () => {
const query = 'METRICS source | STATS var0 = bucket(bytes, 1 hour)';
const query = 'TS source | STATS var0 = bucket(bytes, 1 hour)';
const { root } = parse(query);
const functions: ESQLFunction[] = [];
@ -206,7 +206,7 @@ describe('structurally can walk all nodes', () => {
});
test('iterates through all sources', () => {
const { ast } = parse('METRICS index, index2, index3, index4');
const { ast } = parse('TS index, index2, index3, index4');
const sources: ESQLSource[] = [];
walk(ast, {

View file

@ -65,14 +65,14 @@ describe('esql query helpers', () => {
const idxPattern13 = getIndexPatternFromESQLQuery('ROW a = 1, b = "two", c = null');
expect(idxPattern13).toBe('');
const idxPattern14 = getIndexPatternFromESQLQuery('METRICS tsdb');
const idxPattern14 = getIndexPatternFromESQLQuery('TS tsdb');
expect(idxPattern14).toBe('tsdb');
const idxPattern15 = getIndexPatternFromESQLQuery('METRICS tsdb | STATS max(cpu) BY host');
const idxPattern15 = getIndexPatternFromESQLQuery('TS tsdb | STATS max(cpu) BY host');
expect(idxPattern15).toBe('tsdb');
const idxPattern16 = getIndexPatternFromESQLQuery(
'METRICS pods | STATS load=avg(cpu), writes=max(rate(indexing_requests)) BY pod | SORT pod'
'TS pods | STATS load=avg(cpu), writes=max(rate(indexing_requests)) BY pod | SORT pod'
);
expect(idxPattern16).toBe('pods');
@ -145,12 +145,12 @@ describe('esql query helpers', () => {
).toBeFalsy();
});
it('should return false for metrics with no aggregation', () => {
expect(hasTransformationalCommand('metrics a')).toBeFalsy();
it('should return false for timeseries with no aggregation', () => {
expect(hasTransformationalCommand('ts a')).toBeFalsy();
});
it('should return true for metrics with aggregations', () => {
expect(hasTransformationalCommand('metrics a | stats var = avg(b)')).toBeTruthy();
it('should return true for timeseries with aggregations', () => {
expect(hasTransformationalCommand('ts a | stats var = avg(b)')).toBeTruthy();
});
});

View file

@ -25,7 +25,7 @@ const DEFAULT_ESQL_LIMIT = 1000;
// retrieves the index pattern from the aggregate query for ES|QL using ast parsing
export function getIndexPatternFromESQLQuery(esql?: string) {
const { ast } = parse(esql);
const sourceCommand = ast.find(({ name }) => ['from', 'metrics'].includes(name));
const sourceCommand = ast.find(({ name }) => ['from', 'ts'].includes(name));
const args = (sourceCommand?.args ?? []) as ESQLSource[];
const indices = args.filter((arg) => arg.sourceType === 'index');
return indices?.map((index) => index.name).join(',');

View file

@ -247,14 +247,14 @@ Tests are found in files named with the following convention: `validation.some-d
Here is an example of a block in the new test format.
```ts
describe('METRICS <sources> [ <aggregates> [ BY <grouping> ]]', () => {
describe('TS <sources> [ <aggregates> [ BY <grouping> ]]', () => {
test('errors on invalid command start', async () => {
const { expectErrors } = await setup();
await expectErrors('m', [
"SyntaxError: mismatched input 'm' expecting {'explain', 'from', 'meta', 'metrics', 'row', 'show'}",
"SyntaxError: mismatched input 'm' expecting {'explain', 'from', 'meta', 'ts', 'row', 'show'}",
]);
await expectErrors('metrics ', [
await expectErrors('ts ', [
"SyntaxError: mismatched input '<EOF>' expecting {UNQUOTED_SOURCE, QUOTED_STRING}",
]);
});

View file

@ -82,7 +82,7 @@ describe('autocomplete', () => {
},
});
// const sourceCommands = ['row', 'from', 'show', 'metrics']; Uncomment when metrics is being released
// const sourceCommands = ['row', 'from', 'show', 'ts']; Uncomment when ts is being released
const sourceCommands = ['row', 'from', 'show'];
describe('New command', () => {

View file

@ -207,7 +207,7 @@ export const commandDefinitions: Array<CommandDefinition<any>> = [
suggest: suggestForShow,
},
{
name: 'metrics',
name: 'ts',
hidden: true,
description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.metricsDoc', {
defaultMessage:
@ -219,7 +219,7 @@ export const commandDefinitions: Array<CommandDefinition<any>> = [
'When you perform more than one aggregation, separate each aggregation with a comma.',
}),
declaration: '',
examples: ['METRICS index', 'METRICS index, index2'],
examples: ['TS index', 'TS index, index2'],
suggest: () => [],
},
{

View file

@ -123,7 +123,7 @@ export function isComma(char: string) {
}
export function isSourceCommand({ label }: { label: string }) {
return ['FROM', 'ROW', 'SHOW', 'METRICS'].includes(label);
return ['FROM', 'ROW', 'SHOW', 'TS'].includes(label);
}
let fnLookups: Map<string, FunctionDefinition> | undefined;

View file

@ -142,7 +142,7 @@ export function collectVariables(
})
.on('visitCommand', (ctx) => {
const ret = [];
if (['row', 'eval', 'stats', 'inlinestats', 'metrics', 'rename'].includes(ctx.node.name)) {
if (['row', 'eval', 'stats', 'inlinestats', 'ts', 'rename'].includes(ctx.node.name)) {
ret.push(...ctx.visitArgs());
}
if (['stats', 'inlinestats', 'enrich'].includes(ctx.node.name)) {

View file

@ -9,17 +9,17 @@
import * as helpers from '../helpers';
export const validationMetricsCommandTestSuite = (setup: helpers.Setup) => {
export const validationTimeseriesCommandTestSuite = (setup: helpers.Setup) => {
describe('validation', () => {
describe('command', () => {
describe('METRICS <sources> [ <aggregates> [ BY <grouping> ]]', () => {
describe('TS <sources> [ <aggregates> [ BY <grouping> ]]', () => {
test('errors on invalid command start', async () => {
const { expectErrors } = await setup();
await expectErrors('m', [
"SyntaxError: mismatched input 'm' expecting {'explain', 'row', 'from', 'show'}",
]);
await expectErrors('metrics ', [
await expectErrors('ts ', [
"SyntaxError: mismatched input '<EOF>' expecting {QUOTED_STRING, UNQUOTED_SOURCE}",
]);
});
@ -28,46 +28,44 @@ export const validationMetricsCommandTestSuite = (setup: helpers.Setup) => {
test('no errors on correct indices usage', async () => {
const { expectErrors } = await setup();
await expectErrors('metrics index', []);
await expectErrors('metrics index, other_index', []);
await expectErrors('metrics index, other_index,.secret_index', []);
await expectErrors('metrics .secret_index', []);
await expectErrors('METRICS .secret_index', []);
await expectErrors('mEtRiCs .secret_index', []);
await expectErrors('metrics ind*, other*', []);
await expectErrors('metrics index*', []);
await expectErrors('metrics *a_i*dex*', []);
await expectErrors('metrics in*ex*', []);
await expectErrors('metrics *n*ex', []);
await expectErrors('metrics *n*ex*', []);
await expectErrors('metrics i*d*x*', []);
await expectErrors('metrics i*d*x', []);
await expectErrors('metrics i***x*', []);
await expectErrors('metrics i****', []);
await expectErrors('metrics i**', []);
await expectErrors('metrics index**', []);
await expectErrors('metrics *ex', []);
await expectErrors('metrics *ex*', []);
await expectErrors('metrics in*ex', []);
await expectErrors('metrics ind*ex', []);
await expectErrors('metrics *,-.*', []);
await expectErrors('metrics remote-*:indexes*', ['Unknown index [remote-*:indexes*]']);
await expectErrors('metrics remote-ccs:indexes', [
await expectErrors('ts index', []);
await expectErrors('ts index, other_index', []);
await expectErrors('ts index, other_index,.secret_index', []);
await expectErrors('ts .secret_index', []);
await expectErrors('TS .secret_index', []);
await expectErrors('Ts .secret_index', []);
await expectErrors('ts ind*, other*', []);
await expectErrors('ts index*', []);
await expectErrors('ts *a_i*dex*', []);
await expectErrors('ts in*ex*', []);
await expectErrors('ts *n*ex', []);
await expectErrors('ts *n*ex*', []);
await expectErrors('ts i*d*x*', []);
await expectErrors('ts i*d*x', []);
await expectErrors('ts i***x*', []);
await expectErrors('ts i****', []);
await expectErrors('ts i**', []);
await expectErrors('ts index**', []);
await expectErrors('ts *ex', []);
await expectErrors('ts *ex*', []);
await expectErrors('ts in*ex', []);
await expectErrors('ts ind*ex', []);
await expectErrors('ts *,-.*', []);
await expectErrors('ts remote-*:indexes*', ['Unknown index [remote-*:indexes*]']);
await expectErrors('ts remote-ccs:indexes', ['Unknown index [remote-ccs:indexes]']);
await expectErrors('ts a_index, remote-ccs:indexes', [
'Unknown index [remote-ccs:indexes]',
]);
await expectErrors('metrics a_index, remote-ccs:indexes', [
'Unknown index [remote-ccs:indexes]',
]);
await expectErrors('metrics .secret_index', []);
await expectErrors('ts .secret_index', []);
});
test('errors on trailing comma', async () => {
const { expectErrors } = await setup();
await expectErrors('metrics index,', [
await expectErrors('ts index,', [
"SyntaxError: mismatched input '<EOF>' expecting {QUOTED_STRING, UNQUOTED_SOURCE}",
]);
await expectErrors(`metrics index\n, \tother_index\t,\n \t `, [
await expectErrors(`ts index\n, \tother_index\t,\n \t `, [
"SyntaxError: mismatched input '<EOF>' expecting {QUOTED_STRING, UNQUOTED_SOURCE}",
]);
});
@ -75,21 +73,21 @@ export const validationMetricsCommandTestSuite = (setup: helpers.Setup) => {
test('errors on invalid syntax', async () => {
const { expectErrors } = await setup();
await expectErrors(`metrics index = 1`, [
await expectErrors(`ts index = 1`, [
"SyntaxError: mismatched input '=' expecting <EOF>",
]);
await expectErrors('metrics `index`', ['Unknown index [`index`]']);
await expectErrors('ts `index`', ['Unknown index [`index`]']);
});
test('errors on unknown index', async () => {
const { expectErrors } = await setup();
await expectErrors(`METRICS index, missingIndex`, ['Unknown index [missingIndex]']);
await expectErrors(`METRICS average()`, ['Unknown index [average()]']);
await expectErrors(`metrics custom_function()`, ['Unknown index [custom_function()]']);
await expectErrors(`metrics indexes*`, ['Unknown index [indexes*]']);
await expectErrors('metrics doubleField', ['Unknown index [doubleField]']);
await expectErrors('metrics policy', ['Unknown index [policy]']);
await expectErrors(`TS index, missingIndex`, ['Unknown index [missingIndex]']);
await expectErrors(`TS average()`, ['Unknown index [average()]']);
await expectErrors(`ts custom_function()`, ['Unknown index [custom_function()]']);
await expectErrors(`ts indexes*`, ['Unknown index [indexes*]']);
await expectErrors('ts doubleField', ['Unknown index [doubleField]']);
await expectErrors('ts policy', ['Unknown index [policy]']);
});
});
@ -97,23 +95,23 @@ export const validationMetricsCommandTestSuite = (setup: helpers.Setup) => {
test('no errors on correct usage', async () => {
const { expectErrors } = await setup();
await expectErrors('METRICS a_index | STATS count()', []);
await expectErrors('metrics a_index | STATS avg(doubleField) by 1', []);
await expectErrors('metrics a_index | STATS count(`doubleField`)', []);
await expectErrors('metrics a_index | STATS count(*)', []);
await expectErrors('metrics index | STATS var0 = count(*)', []);
await expectErrors('metrics a_index | STATS var0 = count()', []);
await expectErrors('metrics a_index | STATS var0 = avg(doubleField), count(*)', []);
await expectErrors(`metrics a_index | STATS sum(case(false, 0, 1))`, []);
await expectErrors(`metrics a_index | STATS var0 = sum( case(false, 0, 1))`, []);
await expectErrors('metrics a_index | STATS count(textField == "a" or null)', []);
await expectErrors('metrics other_index | STATS max(doubleField) by textField', []);
await expectErrors('TS a_index | STATS count()', []);
await expectErrors('ts a_index | STATS avg(doubleField) by 1', []);
await expectErrors('ts a_index | STATS count(`doubleField`)', []);
await expectErrors('ts a_index | STATS count(*)', []);
await expectErrors('ts index | STATS var0 = count(*)', []);
await expectErrors('ts a_index | STATS var0 = count()', []);
await expectErrors('ts a_index | STATS var0 = avg(doubleField), count(*)', []);
await expectErrors(`ts a_index | STATS sum(case(false, 0, 1))`, []);
await expectErrors(`ts a_index | STATS var0 = sum( case(false, 0, 1))`, []);
await expectErrors('ts a_index | STATS count(textField == "a" or null)', []);
await expectErrors('ts other_index | STATS max(doubleField) by textField', []);
});
test('syntax errors', async () => {
const { expectErrors } = await setup();
await expectErrors('metrics a_index doubleField=', [
await expectErrors('ts a_index doubleField=', [
"SyntaxError: mismatched input 'doubleField' expecting <EOF>",
]);
});
@ -121,7 +119,7 @@ export const validationMetricsCommandTestSuite = (setup: helpers.Setup) => {
test('errors on unknown function', async () => {
const { expectErrors } = await setup();
await expectErrors('metrics a_index var0 = avg(fn(number)), count(*)', [
await expectErrors('ts a_index var0 = avg(fn(number)), count(*)', [
"SyntaxError: mismatched input 'var0' expecting <EOF>",
]);
});
@ -131,7 +129,7 @@ export const validationMetricsCommandTestSuite = (setup: helpers.Setup) => {
for (const subCommand of ['keep', 'drop', 'eval']) {
await expectErrors(
'metrics a_index | stats count(`doubleField`) | ' +
'TS a_index | stats count(`doubleField`) | ' +
subCommand +
' `count(``doubleField``)` ',
[]
@ -142,10 +140,10 @@ export const validationMetricsCommandTestSuite = (setup: helpers.Setup) => {
test('semantic function validation errors', async () => {
const { expectErrors } = await setup();
await expectErrors('metrics a_index | stats count(round(*))', [
await expectErrors('TS a_index | stats count(round(*))', [
'Using wildcards (*) in round is not allowed',
]);
await expectErrors('metrics a_index | stats count(count(*))', [
await expectErrors('TS a_index | stats count(count(*))', [
`Aggregate function's parameters must be an attribute, literal or a non-aggregation function; found [count(*)] of type [long]`,
]);
});
@ -156,24 +154,21 @@ export const validationMetricsCommandTestSuite = (setup: helpers.Setup) => {
const { expectErrors } = await setup();
await expectErrors(
'metrics a_index | stats avg(doubleField), percentile(doubleField, 50) by ipField',
'TS a_index | stats avg(doubleField), percentile(doubleField, 50) by ipField',
[]
);
await expectErrors(
'metrics a_index | stats avg(doubleField), percentile(doubleField, 50) BY ipField',
'TS a_index | stats avg(doubleField), percentile(doubleField, 50) BY ipField',
[]
);
await expectErrors(
'metrics a_index | stats avg(doubleField), percentile(doubleField, 50) + 1 by ipField',
[]
);
await expectErrors(
'metrics a_index | stats avg(doubleField) by textField | limit 100',
'TS a_index | stats avg(doubleField), percentile(doubleField, 50) + 1 by ipField',
[]
);
await expectErrors('TS a_index | stats avg(doubleField) by textField | limit 100', []);
for (const op of ['+', '-', '*', '/', '%']) {
await expectErrors(
`metrics a_index | stats avg(doubleField) ${op} percentile(doubleField, 50) BY ipField`,
`TS a_index | stats avg(doubleField) ${op} percentile(doubleField, 50) BY ipField`,
[]
);
}
@ -182,22 +177,21 @@ export const validationMetricsCommandTestSuite = (setup: helpers.Setup) => {
test('syntax errors in <aggregates>', async () => {
const { expectErrors } = await setup();
await expectErrors('metrics a_index | stats count(* + 1) BY ipField', [
await expectErrors('TS a_index | stats count(* + 1) BY ipField', [
"SyntaxError: no viable alternative at input 'count(* +'",
]);
await expectErrors('TS a_index \n | stats count(* + round(doubleField)) BY ipField', [
"SyntaxError: no viable alternative at input 'count(* +'",
]);
await expectErrors(
'metrics a_index \n | stats count(* + round(doubleField)) BY ipField',
["SyntaxError: no viable alternative at input 'count(* +'"]
);
});
test('semantic errors in <aggregates>', async () => {
const { expectErrors } = await setup();
await expectErrors('metrics a_index | stats count(round(*)) BY ipField', [
await expectErrors('TS a_index | stats count(round(*)) BY ipField', [
'Using wildcards (*) in round is not allowed',
]);
await expectErrors('metrics a_index | stats count(count(*)) BY ipField', [
await expectErrors('TS a_index | stats count(count(*)) BY ipField', [
`Aggregate function's parameters must be an attribute, literal or a non-aggregation function; found [count(*)] of type [long]`,
]);
});
@ -205,27 +199,25 @@ export const validationMetricsCommandTestSuite = (setup: helpers.Setup) => {
test('errors on unknown field', async () => {
const { expectErrors } = await setup();
await expectErrors('metrics a_index | stats avg(doubleField) by wrongField', [
await expectErrors('TS a_index | stats avg(doubleField) by wrongField', [
'Unknown column [wrongField]',
]);
await expectErrors('metrics a_index | stats avg(doubleField) by wrongField + 1', [
await expectErrors('TS a_index | stats avg(doubleField) by wrongField + 1', [
'Unknown column [wrongField]',
]);
await expectErrors('TS a_index | stats avg(doubleField) by var0 = wrongField + 1', [
'Unknown column [wrongField]',
]);
await expectErrors(
'metrics a_index | stats avg(doubleField) by var0 = wrongField + 1',
['Unknown column [wrongField]']
);
});
test('various errors', async () => {
const { expectErrors } = await setup();
await expectErrors('TS a_index | stats avg(doubleField) by percentile(doubleField)', [
'STATS BY does not support function percentile',
]);
await expectErrors(
'METRICS a_index | stats avg(doubleField) by percentile(doubleField)',
['STATS BY does not support function percentile']
);
await expectErrors(
'METRICS a_index | stats avg(doubleField) by textField, percentile(doubleField) by ipField',
'TS a_index | stats avg(doubleField) by textField, percentile(doubleField) by ipField',
[
"SyntaxError: mismatched input 'by' expecting <EOF>",
'STATS BY does not support function percentile',

View file

@ -8,6 +8,6 @@
*/
import * as helpers from './helpers';
import { validationMetricsCommandTestSuite } from './test_suites/validation.command.metrics';
import { validationTimeseriesCommandTestSuite } from './test_suites/validation.command.metrics';
validationMetricsCommandTestSuite(helpers.setup);
validationTimeseriesCommandTestSuite(helpers.setup);

View file

@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { ESQLAstMetricsCommand, ESQLMessage } from '@kbn/esql-ast';
import { ESQLAstTimeseriesCommand, ESQLMessage } from '@kbn/esql-ast';
import { ESQLFunction, ESQLSource } from '@kbn/esql-ast/src/types';
import {
isAggFunction,
@ -19,18 +19,18 @@ import { isFunctionItem, isLiteralItem } from '../../../..';
import { validateSource } from '../../validation';
/**
* Validates the METRICS source command:
* Validates the TIMESERIES source command:
*
* METRICS <sources>
* TS <sources>
*/
export const validate = (
command: ESQLAstMetricsCommand,
command: ESQLAstTimeseriesCommand,
references: ReferenceMaps
): ESQLMessage[] => {
const messages: ESQLMessage[] = [];
const { sources } = command;
// METRICS <sources> ...
// TS <sources> ...
messages.push(...validateSources(sources, references));
return messages;

View file

@ -205,7 +205,7 @@ export function validateFunction({
forceConstantOnly: allMatchingArgDefinitionsAreConstantOnly || forceConstantOnly,
// use the nesting flag for now just for stats and metrics
// TODO: revisit this part later on to make it more generic
isNested: ['stats', 'inlinestats', 'metrics'].includes(parentCommand)
isNested: ['stats', 'inlinestats', 'ts'].includes(parentCommand)
? isNested || !isAssignment(fn)
: false,
parentAst,

View file

@ -10,7 +10,7 @@
import type {
ESQLAst,
ESQLAstItem,
ESQLAstMetricsCommand,
ESQLAstTimeseriesCommand,
ESQLAstQueryExpression,
ESQLColumn,
ESQLMessage,
@ -30,10 +30,10 @@ export function buildQueryForFieldsFromSource(queryString: string, ast: ESQLAst)
const sources: ESQLSource[] = [];
const metadataFields: ESQLColumn[] = [];
if (firstCommand.name === 'metrics') {
const metrics = firstCommand as ESQLAstMetricsCommand;
if (firstCommand.name === 'ts') {
const timeseries = firstCommand as ESQLAstTimeseriesCommand;
sources.push(...metrics.sources);
sources.push(...timeseries.sources);
} else if (firstCommand.name === 'from') {
const fromSources = mutate.commands.from.sources.list(firstCommand as any);
const fromMetadataColumns = [...mutate.commands.from.metadata.list(firstCommand as any)].map(

View file

@ -10,7 +10,7 @@
import {
AstProviderFn,
ESQLAst,
ESQLAstMetricsCommand,
ESQLAstTimeseriesCommand,
ESQLColumn,
ESQLCommand,
ESQLCommandOption,
@ -53,7 +53,7 @@ import type {
} from './types';
import { validate as validateJoinCommand } from './commands/join';
import { validate as validateMetricsCommand } from './commands/metrics';
import { validate as validateTimeseriesCommand } from './commands/metrics';
/**
* ES|QL validation public API
@ -213,9 +213,9 @@ function validateCommand(
}
switch (commandDef.name) {
case 'metrics': {
const metrics = command as ESQLAstMetricsCommand;
const metricsCommandErrors = validateMetricsCommand(metrics, references);
case 'ts': {
const metrics = command as ESQLAstTimeseriesCommand;
const metricsCommandErrors = validateTimeseriesCommand(metrics, references);
messages.push(...metricsCommandErrors);
break;
}

View file

@ -51,7 +51,8 @@ export const buildESQLTheme = ({
// commands
...buildRuleGroup(
[
'dev_metrics',
'dev_time_series',
'dev_rerank',
'dev_fork',
'metadata',
'mv_expand',