mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[ES|QL] AST/parser support for double param ??
(#215337)
## Summary - Adds support for double param: (1) `??` unnamed; (2) `??name` named; and (3) `??123` positional. - In the `@kbn/esql-ast` package. - Adds types, parsing, builder and pretty-printing support. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
This commit is contained in:
parent
c8abafc6e7
commit
a94ff02ba0
7 changed files with 345 additions and 180 deletions
|
@ -308,6 +308,20 @@ describe('param', () => {
|
|||
expect(text).toBe('?');
|
||||
expect(node).toMatchObject({
|
||||
type: 'literal',
|
||||
paramKind: '?',
|
||||
literalType: 'param',
|
||||
paramType: 'unnamed',
|
||||
});
|
||||
});
|
||||
|
||||
test('unnamed (double)', () => {
|
||||
const node = Builder.param.build('??');
|
||||
const text = BasicPrettyPrinter.expression(node);
|
||||
|
||||
expect(text).toBe('??');
|
||||
expect(node).toMatchObject({
|
||||
type: 'literal',
|
||||
paramKind: '??',
|
||||
literalType: 'param',
|
||||
paramType: 'unnamed',
|
||||
});
|
||||
|
@ -320,6 +334,21 @@ describe('param', () => {
|
|||
expect(text).toBe('?the_name');
|
||||
expect(node).toMatchObject({
|
||||
type: 'literal',
|
||||
paramKind: '?',
|
||||
literalType: 'param',
|
||||
paramType: 'named',
|
||||
value: 'the_name',
|
||||
});
|
||||
});
|
||||
|
||||
test('named (double)', () => {
|
||||
const node = Builder.param.build('??the_name');
|
||||
const text = BasicPrettyPrinter.expression(node);
|
||||
|
||||
expect(text).toBe('??the_name');
|
||||
expect(node).toMatchObject({
|
||||
type: 'literal',
|
||||
paramKind: '??',
|
||||
literalType: 'param',
|
||||
paramType: 'named',
|
||||
value: 'the_name',
|
||||
|
@ -333,6 +362,21 @@ describe('param', () => {
|
|||
expect(text).toBe('?123');
|
||||
expect(node).toMatchObject({
|
||||
type: 'literal',
|
||||
paramKind: '?',
|
||||
literalType: 'param',
|
||||
paramType: 'positional',
|
||||
value: 123,
|
||||
});
|
||||
});
|
||||
|
||||
test('positional (double)', () => {
|
||||
const node = Builder.param.build('??123');
|
||||
const text = BasicPrettyPrinter.expression(node);
|
||||
|
||||
expect(text).toBe('??123');
|
||||
expect(node).toMatchObject({
|
||||
type: 'literal',
|
||||
paramKind: '??',
|
||||
literalType: 'param',
|
||||
paramType: 'positional',
|
||||
value: 123,
|
||||
|
|
|
@ -39,6 +39,7 @@ import {
|
|||
ESQLBooleanLiteral,
|
||||
ESQLNullLiteral,
|
||||
BinaryExpressionOperator,
|
||||
ESQLParamKinds,
|
||||
} from '../types';
|
||||
import { AstNodeParserFields, AstNodeTemplate, PartialFields } from './types';
|
||||
|
||||
|
@ -454,9 +455,14 @@ export namespace Builder {
|
|||
};
|
||||
|
||||
export namespace param {
|
||||
export const unnamed = (fromParser?: Partial<AstNodeParserFields>): ESQLParam => {
|
||||
export const unnamed = (
|
||||
fromParser?: Partial<AstNodeParserFields>,
|
||||
template?: Partial<Pick<ESQLParam, 'paramKind'>>
|
||||
): ESQLParam => {
|
||||
const node = {
|
||||
...Builder.parserFields(fromParser),
|
||||
paramKind: '?',
|
||||
...template,
|
||||
name: '',
|
||||
value: '',
|
||||
paramType: 'unnamed',
|
||||
|
@ -468,10 +474,15 @@ export namespace Builder {
|
|||
};
|
||||
|
||||
export const named = (
|
||||
template: Omit<AstNodeTemplate<ESQLNamedParamLiteral>, 'name' | 'literalType' | 'paramType'>,
|
||||
template: Omit<
|
||||
AstNodeTemplate<ESQLNamedParamLiteral>,
|
||||
'name' | 'literalType' | 'paramType' | 'paramKind'
|
||||
> &
|
||||
Partial<Pick<ESQLNamedParamLiteral, 'paramKind'>>,
|
||||
fromParser?: Partial<AstNodeParserFields>
|
||||
): ESQLNamedParamLiteral => {
|
||||
const node: ESQLNamedParamLiteral = {
|
||||
paramKind: '?',
|
||||
...template,
|
||||
...Builder.parserFields(fromParser),
|
||||
name: '',
|
||||
|
@ -486,11 +497,13 @@ export namespace Builder {
|
|||
export const positional = (
|
||||
template: Omit<
|
||||
AstNodeTemplate<ESQLPositionalParamLiteral>,
|
||||
'name' | 'literalType' | 'paramType'
|
||||
>,
|
||||
'name' | 'literalType' | 'paramType' | 'paramKind'
|
||||
> &
|
||||
Partial<Pick<ESQLPositionalParamLiteral, 'paramKind'>>,
|
||||
fromParser?: Partial<AstNodeParserFields>
|
||||
): ESQLPositionalParamLiteral => {
|
||||
const node: ESQLPositionalParamLiteral = {
|
||||
paramKind: '?',
|
||||
...template,
|
||||
...Builder.parserFields(fromParser),
|
||||
name: '',
|
||||
|
@ -507,18 +520,29 @@ export namespace Builder {
|
|||
options: Partial<ESQLParamLiteral> = {},
|
||||
fromParser?: Partial<AstNodeParserFields>
|
||||
): ESQLParam => {
|
||||
const value: string = name.startsWith('?') ? name.slice(1) : name;
|
||||
let paramKind: ESQLParamKinds = options.paramKind ?? '?';
|
||||
|
||||
if (name.startsWith('??')) {
|
||||
paramKind = '??';
|
||||
} else if (name.startsWith('?')) {
|
||||
paramKind = '?';
|
||||
}
|
||||
|
||||
const value: string = name.startsWith('?') ? name.slice(paramKind === '?' ? 1 : 2) : name;
|
||||
|
||||
if (!value) {
|
||||
return Builder.param.unnamed(options);
|
||||
return Builder.param.unnamed(options, { paramKind });
|
||||
}
|
||||
|
||||
const isNumeric = !isNaN(Number(value)) && String(Number(value)) === value;
|
||||
|
||||
if (isNumeric) {
|
||||
return Builder.param.positional({ ...options, value: Number(value) }, fromParser);
|
||||
return Builder.param.positional(
|
||||
{ ...options, paramKind, value: Number(value) },
|
||||
fromParser
|
||||
);
|
||||
} else {
|
||||
return Builder.param.named({ ...options, value }, fromParser);
|
||||
return Builder.param.named({ ...options, paramKind, value }, fromParser);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -10,168 +10,246 @@
|
|||
import { parse } from '..';
|
||||
import { Walker } from '../../walker';
|
||||
|
||||
/**
|
||||
* Un-named parameters are represented by a question mark "?".
|
||||
*/
|
||||
describe('un-named parameters', () => {
|
||||
describe('correctly formatted', () => {
|
||||
it('can parse a single un-named param', () => {
|
||||
const query = 'ROW x = ?';
|
||||
const { ast, errors } = parse(query);
|
||||
const params = Walker.params(ast);
|
||||
describe('single ? param', () => {
|
||||
/**
|
||||
* Un-named parameters are represented by a question mark "?".
|
||||
*/
|
||||
describe('un-named parameters', () => {
|
||||
describe('correctly formatted', () => {
|
||||
it('can parse a single un-named param', () => {
|
||||
const query = 'ROW x = ?';
|
||||
const { ast, errors } = parse(query);
|
||||
const params = Walker.params(ast);
|
||||
|
||||
expect(errors.length).toBe(0);
|
||||
expect(params).toMatchObject([
|
||||
{
|
||||
type: 'literal',
|
||||
literalType: 'param',
|
||||
paramType: 'unnamed',
|
||||
location: {
|
||||
min: 8,
|
||||
max: 8,
|
||||
expect(errors.length).toBe(0);
|
||||
expect(params).toMatchObject([
|
||||
{
|
||||
type: 'literal',
|
||||
literalType: 'param',
|
||||
paramType: 'unnamed',
|
||||
location: {
|
||||
min: 8,
|
||||
max: 8,
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
]);
|
||||
|
||||
const { min, max } = params[0].location;
|
||||
const { min, max } = params[0].location;
|
||||
|
||||
expect(query.slice(min, max + 1)).toBe('?');
|
||||
expect(query.slice(min, max + 1)).toBe('?');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Positional parameters are represented by a question mark followed by a number "?1".
|
||||
*/
|
||||
describe('positional parameters', () => {
|
||||
describe('correctly formatted', () => {
|
||||
it('can parse a single positional param', () => {
|
||||
const query = 'ROW x = ?1';
|
||||
const { ast, errors } = parse(query);
|
||||
const params = Walker.params(ast);
|
||||
/**
|
||||
* Positional parameters are represented by a question mark followed by a number "?1".
|
||||
*/
|
||||
describe('positional parameters', () => {
|
||||
describe('correctly formatted', () => {
|
||||
it('can parse a single positional param', () => {
|
||||
const query = 'ROW x = ?1';
|
||||
const { ast, errors } = parse(query);
|
||||
const params = Walker.params(ast);
|
||||
|
||||
expect(errors.length).toBe(0);
|
||||
expect(params).toMatchObject([
|
||||
{
|
||||
type: 'literal',
|
||||
literalType: 'param',
|
||||
paramType: 'positional',
|
||||
value: 1,
|
||||
location: {
|
||||
min: 8,
|
||||
max: 9,
|
||||
expect(errors.length).toBe(0);
|
||||
expect(params).toMatchObject([
|
||||
{
|
||||
type: 'literal',
|
||||
literalType: 'param',
|
||||
paramType: 'positional',
|
||||
value: 1,
|
||||
location: {
|
||||
min: 8,
|
||||
max: 9,
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
]);
|
||||
|
||||
const { min, max } = params[0].location;
|
||||
const { min, max } = params[0].location;
|
||||
|
||||
expect(query.slice(min, max + 1)).toBe('?1');
|
||||
expect(query.slice(min, max + 1)).toBe('?1');
|
||||
});
|
||||
|
||||
it('multiple positional params', () => {
|
||||
const query = 'ROW x = ?5, x2 = ?5, y = ?6, z = ?7';
|
||||
const { ast, errors } = parse(query);
|
||||
const params = Walker.params(ast);
|
||||
|
||||
expect(errors.length).toBe(0);
|
||||
expect(params.length).toBe(4);
|
||||
params.sort((a, b) => a.location.min - b.location.min);
|
||||
expect(params).toMatchObject([
|
||||
{
|
||||
type: 'literal',
|
||||
literalType: 'param',
|
||||
paramType: 'positional',
|
||||
value: 5,
|
||||
},
|
||||
{
|
||||
type: 'literal',
|
||||
literalType: 'param',
|
||||
paramType: 'positional',
|
||||
value: 5,
|
||||
},
|
||||
{
|
||||
type: 'literal',
|
||||
literalType: 'param',
|
||||
paramType: 'positional',
|
||||
value: 6,
|
||||
},
|
||||
{
|
||||
type: 'literal',
|
||||
literalType: 'param',
|
||||
paramType: 'positional',
|
||||
value: 7,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Named parameters are represented by a question mark followed by a name "?name".
|
||||
*/
|
||||
describe('named parameters', () => {
|
||||
describe('correctly formatted', () => {
|
||||
it('can parse a single named param', () => {
|
||||
const query = 'ROW x = ?theName';
|
||||
const { ast, errors } = parse(query);
|
||||
const params = Walker.params(ast);
|
||||
|
||||
expect(errors.length).toBe(0);
|
||||
expect(params).toMatchObject([
|
||||
{
|
||||
type: 'literal',
|
||||
literalType: 'param',
|
||||
paramType: 'named',
|
||||
value: 'theName',
|
||||
location: {
|
||||
min: 8,
|
||||
max: 15,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const { min, max } = params[0].location;
|
||||
|
||||
expect(query.slice(min, max + 1)).toBe('?theName');
|
||||
});
|
||||
});
|
||||
|
||||
it('multiple positional params', () => {
|
||||
const query = 'ROW x = ?5, x2 = ?5, y = ?6, z = ?7';
|
||||
it('multiple named params', () => {
|
||||
const query = 'ROW x = ?a, y = ?b, z = ?c';
|
||||
const { ast, errors } = parse(query);
|
||||
const params = Walker.params(ast);
|
||||
|
||||
expect(errors.length).toBe(0);
|
||||
expect(params.length).toBe(4);
|
||||
expect(params.length).toBe(3);
|
||||
params.sort((a, b) => a.location.min - b.location.min);
|
||||
expect(params).toMatchObject([
|
||||
{
|
||||
type: 'literal',
|
||||
literalType: 'param',
|
||||
paramType: 'positional',
|
||||
value: 5,
|
||||
paramType: 'named',
|
||||
value: 'a',
|
||||
},
|
||||
{
|
||||
type: 'literal',
|
||||
literalType: 'param',
|
||||
paramType: 'positional',
|
||||
value: 5,
|
||||
},
|
||||
{
|
||||
type: 'literal',
|
||||
literalType: 'param',
|
||||
paramType: 'positional',
|
||||
value: 6,
|
||||
},
|
||||
{
|
||||
type: 'literal',
|
||||
literalType: 'param',
|
||||
paramType: 'positional',
|
||||
value: 7,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Named parameters are represented by a question mark followed by a name "?name".
|
||||
*/
|
||||
describe('named parameters', () => {
|
||||
describe('correctly formatted', () => {
|
||||
it('can parse a single named param', () => {
|
||||
const query = 'ROW x = ?theName';
|
||||
const { ast, errors } = parse(query);
|
||||
const params = Walker.params(ast);
|
||||
|
||||
expect(errors.length).toBe(0);
|
||||
expect(params).toMatchObject([
|
||||
{
|
||||
type: 'literal',
|
||||
literalType: 'param',
|
||||
paramType: 'named',
|
||||
value: 'theName',
|
||||
location: {
|
||||
min: 8,
|
||||
max: 15,
|
||||
},
|
||||
value: 'b',
|
||||
},
|
||||
{
|
||||
type: 'literal',
|
||||
literalType: 'param',
|
||||
paramType: 'named',
|
||||
value: 'c',
|
||||
},
|
||||
]);
|
||||
|
||||
const { min, max } = params[0].location;
|
||||
|
||||
expect(query.slice(min, max + 1)).toBe('?theName');
|
||||
});
|
||||
});
|
||||
|
||||
it('multiple named params', () => {
|
||||
const query = 'ROW x = ?a, y = ?b, z = ?c';
|
||||
const { ast, errors } = parse(query);
|
||||
const params = Walker.params(ast);
|
||||
|
||||
expect(errors.length).toBe(0);
|
||||
expect(params.length).toBe(3);
|
||||
params.sort((a, b) => a.location.min - b.location.min);
|
||||
expect(params).toMatchObject([
|
||||
{
|
||||
type: 'literal',
|
||||
literalType: 'param',
|
||||
paramType: 'named',
|
||||
value: 'a',
|
||||
},
|
||||
{
|
||||
type: 'literal',
|
||||
literalType: 'param',
|
||||
paramType: 'named',
|
||||
value: 'b',
|
||||
},
|
||||
{
|
||||
type: 'literal',
|
||||
literalType: 'param',
|
||||
paramType: 'named',
|
||||
value: 'c',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
describe('when incorrectly formatted, returns errors', () => {
|
||||
it('three question marks "?" in a row', () => {
|
||||
const text = 'ROW x = ???';
|
||||
const { errors } = parse(text);
|
||||
expect(errors.length > 0).toBe(true);
|
||||
describe('when incorrectly formatted, returns errors', () => {
|
||||
it('three question marks "?" in a row', () => {
|
||||
const text = 'ROW x = ???';
|
||||
const { errors } = parse(text);
|
||||
expect(errors.length > 0).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('double ?? param', () => {
|
||||
describe('unnamed parameters', () => {
|
||||
describe('correctly formatted', () => {
|
||||
it('can parse a single named param', () => {
|
||||
const query = 'ROW ??';
|
||||
const { ast, errors } = parse(query);
|
||||
const params = Walker.params(ast);
|
||||
|
||||
expect(errors.length).toBe(0);
|
||||
expect(params).toMatchObject([
|
||||
{
|
||||
type: 'literal',
|
||||
paramKind: '??',
|
||||
literalType: 'param',
|
||||
paramType: 'unnamed',
|
||||
},
|
||||
]);
|
||||
|
||||
const { min, max } = params[0].location;
|
||||
|
||||
expect(query.slice(min, max + 1)).toBe('??');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('named parameters', () => {
|
||||
describe('correctly formatted', () => {
|
||||
it('can parse a single named param', () => {
|
||||
const query = 'ROW ??theName';
|
||||
const { ast, errors } = parse(query);
|
||||
const params = Walker.params(ast);
|
||||
|
||||
expect(errors.length).toBe(0);
|
||||
expect(params).toMatchObject([
|
||||
{
|
||||
type: 'literal',
|
||||
paramKind: '??',
|
||||
literalType: 'param',
|
||||
paramType: 'named',
|
||||
value: 'theName',
|
||||
},
|
||||
]);
|
||||
|
||||
const { min, max } = params[0].location;
|
||||
|
||||
expect(query.slice(min, max + 1)).toBe('??theName');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('positional parameters', () => {
|
||||
describe('correctly formatted', () => {
|
||||
it('can parse a single named param', () => {
|
||||
const query = 'ROW ??123';
|
||||
const { ast, errors } = parse(query);
|
||||
const params = Walker.params(ast);
|
||||
|
||||
expect(errors.length).toBe(0);
|
||||
expect(params).toMatchObject([
|
||||
{
|
||||
type: 'literal',
|
||||
paramKind: '??',
|
||||
literalType: 'param',
|
||||
paramType: 'positional',
|
||||
value: 123,
|
||||
},
|
||||
]);
|
||||
|
||||
const { min, max } = params[0].location;
|
||||
|
||||
expect(query.slice(min, max + 1)).toBe('??123');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -33,6 +33,8 @@ import {
|
|||
InputNamedOrPositionalParamContext,
|
||||
IdentifierOrParameterContext,
|
||||
StringContext,
|
||||
InputNamedOrPositionalDoubleParamsContext,
|
||||
InputDoubleParamsContext,
|
||||
} from '../antlr/esql_parser';
|
||||
import { DOUBLE_TICKS_REGEX, SINGLE_BACKTICK, TICKS_REGEX } from './constants';
|
||||
import type {
|
||||
|
@ -59,6 +61,7 @@ import type {
|
|||
ESQLBinaryExpression,
|
||||
BinaryExpressionOperator,
|
||||
ESQLCommand,
|
||||
ESQLParamKinds,
|
||||
} from '../types';
|
||||
import { parseIdentifier, getPosition } from './helpers';
|
||||
import { Builder, type AstNodeParserFields } from '../builder';
|
||||
|
@ -289,13 +292,15 @@ export const createBinaryExpression = (
|
|||
|
||||
export const createIdentifierOrParam = (ctx: IdentifierOrParameterContext) => {
|
||||
const identifier = ctx.identifier();
|
||||
|
||||
if (identifier) {
|
||||
return createIdentifier(identifier);
|
||||
} else {
|
||||
const parameter = ctx.parameter();
|
||||
if (parameter) {
|
||||
return createParam(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
const parameter = ctx.parameter() ?? ctx.doubleParameter();
|
||||
|
||||
if (parameter) {
|
||||
return createParam(parameter);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -307,19 +312,27 @@ export const createIdentifier = (identifier: IdentifierContext): ESQLIdentifier
|
|||
};
|
||||
|
||||
export const createParam = (ctx: ParseTree) => {
|
||||
if (ctx instanceof InputParamContext) {
|
||||
return Builder.param.unnamed(createParserFields(ctx));
|
||||
} else if (ctx instanceof InputNamedOrPositionalParamContext) {
|
||||
if (ctx instanceof InputParamContext || ctx instanceof InputDoubleParamsContext) {
|
||||
const isDoubleParam = ctx instanceof InputDoubleParamsContext;
|
||||
const paramKind: ESQLParamKinds = isDoubleParam ? '??' : '?';
|
||||
|
||||
return Builder.param.unnamed(createParserFields(ctx), { paramKind });
|
||||
} else if (
|
||||
ctx instanceof InputNamedOrPositionalParamContext ||
|
||||
ctx instanceof InputNamedOrPositionalDoubleParamsContext
|
||||
) {
|
||||
const isDoubleParam = ctx instanceof InputNamedOrPositionalDoubleParamsContext;
|
||||
const paramKind: ESQLParamKinds = isDoubleParam ? '??' : '?';
|
||||
const text = ctx.getText();
|
||||
const value = text.slice(1);
|
||||
const value = text.slice(isDoubleParam ? 2 : 1);
|
||||
const valueAsNumber = Number(value);
|
||||
const isPositional = String(valueAsNumber) === value;
|
||||
const parserFields = createParserFields(ctx);
|
||||
|
||||
if (isPositional) {
|
||||
return Builder.param.positional({ value: valueAsNumber }, parserFields);
|
||||
return Builder.param.positional({ paramKind, value: valueAsNumber }, parserFields);
|
||||
} else {
|
||||
return Builder.param.named({ value }, parserFields);
|
||||
return Builder.param.named({ paramKind, value }, parserFields);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -310,8 +310,7 @@ function visitOperatorExpression(
|
|||
fn.args.push(arg);
|
||||
}
|
||||
return fn;
|
||||
}
|
||||
if (ctx instanceof ArithmeticBinaryContext) {
|
||||
} else if (ctx instanceof ArithmeticBinaryContext) {
|
||||
const fn = createFunction(getMathOperation(ctx), ctx, undefined, 'binary-expression');
|
||||
const args = [visitOperatorExpression(ctx._left), visitOperatorExpression(ctx._right)];
|
||||
for (const arg of args) {
|
||||
|
@ -323,8 +322,7 @@ function visitOperatorExpression(
|
|||
const argsLocationExtends = computeLocationExtends(fn);
|
||||
fn.location = argsLocationExtends;
|
||||
return fn;
|
||||
}
|
||||
if (ctx instanceof OperatorExpressionDefaultContext) {
|
||||
} else if (ctx instanceof OperatorExpressionDefaultContext) {
|
||||
return visitPrimaryExpression(ctx.primaryExpression());
|
||||
}
|
||||
}
|
||||
|
@ -424,14 +422,11 @@ export function visitRenameClauses(clausesCtx: RenameClauseContext[]): ESQLAstIt
|
|||
export function visitPrimaryExpression(ctx: PrimaryExpressionContext): ESQLAstItem | ESQLAstItem[] {
|
||||
if (ctx instanceof ConstantDefaultContext) {
|
||||
return getConstant(ctx.constant());
|
||||
}
|
||||
if (ctx instanceof DereferenceContext) {
|
||||
} else if (ctx instanceof DereferenceContext) {
|
||||
return createColumn(ctx.qualifiedName());
|
||||
}
|
||||
if (ctx instanceof ParenthesizedExpressionContext) {
|
||||
} else if (ctx instanceof ParenthesizedExpressionContext) {
|
||||
return collectBooleanExpression(ctx.booleanExpression());
|
||||
}
|
||||
if (ctx instanceof FunctionContext) {
|
||||
} else if (ctx instanceof FunctionContext) {
|
||||
const functionExpressionCtx = ctx.functionExpression();
|
||||
const fn = createFunctionCall(ctx);
|
||||
const asteriskArg = functionExpressionCtx.ASTERISK()
|
||||
|
@ -448,8 +443,7 @@ export function visitPrimaryExpression(ctx: PrimaryExpressionContext): ESQLAstIt
|
|||
fn.args.push(...functionArgs);
|
||||
}
|
||||
return fn;
|
||||
}
|
||||
if (ctx instanceof InlineCastContext) {
|
||||
} else if (ctx instanceof InlineCastContext) {
|
||||
return collectInlineCast(ctx);
|
||||
}
|
||||
return createUnknownItem(ctx);
|
||||
|
|
|
@ -127,12 +127,14 @@ export const LeafPrinter = {
|
|||
},
|
||||
|
||||
param: (node: ESQLParamLiteral) => {
|
||||
const paramKind = node.paramKind || '?';
|
||||
|
||||
switch (node.paramType) {
|
||||
case 'named':
|
||||
case 'positional':
|
||||
return '?' + node.value;
|
||||
return paramKind + node.value;
|
||||
default:
|
||||
return '?';
|
||||
return paramKind;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -387,29 +387,52 @@ export interface ESQLStringLiteral extends ESQLAstBaseItem {
|
|||
}
|
||||
|
||||
// @internal
|
||||
export interface ESQLParamLiteral<ParamType extends string = string> extends ESQLAstBaseItem {
|
||||
export interface ESQLParamLiteral<
|
||||
ParamType extends string = string,
|
||||
ParamKind extends ESQLParamKinds = ESQLParamKinds
|
||||
> extends ESQLAstBaseItem {
|
||||
type: 'literal';
|
||||
literalType: 'param';
|
||||
paramKind: ParamKind;
|
||||
paramType: ParamType;
|
||||
value: string | number;
|
||||
}
|
||||
|
||||
export type ESQLParamKinds = '?' | '??';
|
||||
|
||||
/**
|
||||
* *Unnamed* parameter is not named, just a question mark "?".
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export type ESQLUnnamedParamLiteral = ESQLParamLiteral<'unnamed'>;
|
||||
export type ESQLUnnamedParamLiteral<ParamKind extends ESQLParamKinds = ESQLParamKinds> =
|
||||
ESQLParamLiteral<'unnamed', ParamKind>;
|
||||
|
||||
/**
|
||||
* *Named* parameter is a question mark followed by a name "?name".
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export interface ESQLNamedParamLiteral extends ESQLParamLiteral<'named'> {
|
||||
export interface ESQLNamedParamLiteral<ParamKind extends ESQLParamKinds = ESQLParamKinds>
|
||||
extends ESQLParamLiteral<'named', ParamKind> {
|
||||
value: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* *Positional* parameter is a question mark followed by a number "?1".
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export interface ESQLPositionalParamLiteral<ParamKind extends ESQLParamKinds = ESQLParamKinds>
|
||||
extends ESQLParamLiteral<'positional', ParamKind> {
|
||||
value: number;
|
||||
}
|
||||
|
||||
export type ESQLParam =
|
||||
| ESQLUnnamedParamLiteral
|
||||
| ESQLNamedParamLiteral
|
||||
| ESQLPositionalParamLiteral;
|
||||
|
||||
export interface ESQLIdentifier extends ESQLAstBaseItem {
|
||||
type: 'identifier';
|
||||
}
|
||||
|
@ -418,19 +441,6 @@ export const isESQLNamedParamLiteral = (node: ESQLAstItem): node is ESQLNamedPar
|
|||
isESQLAstBaseItem(node) &&
|
||||
(node as ESQLNamedParamLiteral).literalType === 'param' &&
|
||||
(node as ESQLNamedParamLiteral).paramType === 'named';
|
||||
/**
|
||||
* *Positional* parameter is a question mark followed by a number "?1".
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export interface ESQLPositionalParamLiteral extends ESQLParamLiteral<'positional'> {
|
||||
value: number;
|
||||
}
|
||||
|
||||
export type ESQLParam =
|
||||
| ESQLUnnamedParamLiteral
|
||||
| ESQLNamedParamLiteral
|
||||
| ESQLPositionalParamLiteral;
|
||||
|
||||
export interface ESQLMessage {
|
||||
type: 'error' | 'warning';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue