[ES|QL] Add support for client-side INLINESTATS validation (#189827)

## Summary

Addresses https://github.com/elastic/kibana/issues/189356. This PR adds
support for client-side INLINESTATS validation in the text based editor.
Changes include:
- Add INLINESTATS context in AST package
- Update function definitions to include INLINESTATS
- Hide INLINESTATS from list of suggestions (which we will revert after
command is merged in tech preview)

Before:

![image](https://github.com/user-attachments/assets/18d903ea-5fc9-420f-9c50-d83eb1e40f6c)

After: 


https://github.com/user-attachments/assets/4723735d-ab40-49c3-a1d9-51482c6fb441



### Checklist

Delete any items that are not applicable to this PR.

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [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
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [ ] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [ ] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)


### Risk Matrix

Delete this section if it is not applicable to this PR.

Before closing this PR, invite QA, stakeholders, and other developers to
identify risks that should be tested prior to the change/feature
release.

When forming the risk matrix, consider some of the following examples
and how they may potentially impact the change:

| Risk | Probability | Severity | Mitigation/Notes |

|---------------------------|-------------|----------|-------------------------|
| Multiple Spaces—unexpected behavior in non-default Kibana Space.
| Low | High | Integration tests will verify that all features are still
supported in non-default Kibana Space and when user switches between
spaces. |
| Multiple nodes—Elasticsearch polling might have race conditions
when multiple Kibana nodes are polling for the same tasks. | High | Low
| Tasks are idempotent, so executing them multiple times will not result
in logical error, but will degrade performance. To test for this case we
add plenty of unit tests around this logic and document manual testing
procedure. |
| Code should gracefully handle cases when feature X or plugin Y are
disabled. | Medium | High | Unit tests will verify that any feature flag
or plugin combination still results in our service operational. |
| [See more potential risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx) |


### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: Stratoula Kalafateli <efstratia.kalafateli@elastic.co>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Quynh Nguyen (Quinn) 2024-08-08 10:51:05 -05:00 committed by GitHub
parent 83e78ea5da
commit a5d0321f25
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 732 additions and 278 deletions

View file

@ -18,6 +18,7 @@ import {
EuiFormRow,
EuiButton,
} from '@elastic/eui';
import { EuiProvider } from '@elastic/eui';
import type { CoreStart } from '@kbn/core/public';
@ -42,48 +43,50 @@ export const App = (props: { core: CoreStart; plugins: StartDependencies }) => {
};
return (
<EuiPage>
<EuiPageBody style={{ maxWidth: 800, margin: '0 auto' }}>
<EuiPageHeader paddingSize="s" bottomBorder={true} pageTitle="ES|QL AST Inspector" />
<EuiPageSection paddingSize="s">
<p>This app gives you the AST for a particular ES|QL query.</p>
<EuiProvider>
<EuiPage>
<EuiPageBody style={{ maxWidth: 800, margin: '0 auto' }}>
<EuiPageHeader paddingSize="s" bottomBorder={true} pageTitle="ES|QL AST Inspector" />
<EuiPageSection paddingSize="s">
<p>This app gives you the AST for a particular ES|QL query.</p>
<EuiSpacer />
<EuiSpacer />
<EuiForm>
<EuiFormRow
fullWidth
label="Query"
isInvalid={Boolean(currentErrors.length)}
error={currentErrors.map((error) => error.message)}
>
<EuiTextArea
inputRef={(node) => {
inputRef.current = node;
}}
isInvalid={Boolean(currentErrors.length)}
<EuiForm>
<EuiFormRow
fullWidth
value={currentQuery}
onChange={(e) => setQuery(e.target.value)}
css={{
height: '5em',
}}
/>
</EuiFormRow>
<EuiFormRow fullWidth>
<EuiButton fullWidth onClick={() => parseQuery(inputRef.current?.value ?? '')}>
Parse
</EuiButton>
</EuiFormRow>
</EuiForm>
<EuiSpacer />
<CodeEditor
allowFullScreen={true}
languageId={'json'}
value={JSON.stringify(ast, null, 2)}
/>
</EuiPageSection>
</EuiPageBody>
</EuiPage>
label="Query"
isInvalid={Boolean(currentErrors.length)}
error={currentErrors.map((error) => error.message)}
>
<EuiTextArea
inputRef={(node) => {
inputRef.current = node;
}}
isInvalid={Boolean(currentErrors.length)}
fullWidth
value={currentQuery}
onChange={(e) => setQuery(e.target.value)}
css={{
height: '5em',
}}
/>
</EuiFormRow>
<EuiFormRow fullWidth>
<EuiButton fullWidth onClick={() => parseQuery(inputRef.current?.value ?? '')}>
Parse
</EuiButton>
</EuiFormRow>
</EuiForm>
<EuiSpacer />
<CodeEditor
allowFullScreen={true}
languageId={'json'}
value={JSON.stringify(ast, null, 2)}
/>
</EuiPageSection>
</EuiPageBody>
</EuiPage>
</EuiProvider>
);
};

View file

@ -30,6 +30,7 @@ import {
type MetaCommandContext,
type MetricsCommandContext,
IndexPatternContext,
InlinestatsCommandContext,
} from './antlr/esql_parser';
import { default as ESQLParserListener } from './antlr/esql_parser_listener';
import {
@ -197,6 +198,23 @@ export class AstListener implements ESQLParserListener {
}
}
/**
* Exit a parse tree produced by `esql_parser.inlinestatsCommand`.
* @param ctx the parse tree
*/
exitInlinestatsCommand(ctx: InlinestatsCommandContext) {
const command = createCommand('inlinestats', ctx);
this.ast.push(command);
// STATS expression is optional
if (ctx._stats) {
command.args.push(...collectAllFields(ctx.fields(0)));
}
if (ctx._grouping) {
command.args.push(...visitByOption(ctx, ctx._stats ? ctx.fields(1) : ctx.fields(0)));
}
}
/**
* Exit a parse tree produced by `esql_parser.limitCommand`.
* @param ctx the parse tree

View file

@ -61,6 +61,7 @@ import {
InputNamedOrPositionalParamContext,
InputParamContext,
IndexPatternContext,
InlinestatsCommandContext,
} from './antlr/esql_parser';
import {
createSource,
@ -594,7 +595,10 @@ export function collectAllFields(ctx: FieldsContext | undefined): ESQLAstField[]
return ast;
}
export function visitByOption(ctx: StatsCommandContext, expr: FieldsContext | undefined) {
export function visitByOption(
ctx: StatsCommandContext | InlinestatsCommandContext,
expr: FieldsContext | undefined
) {
if (!ctx.BY() || !expr) {
return [];
}

View file

@ -118,11 +118,10 @@ export class GlobalVisitorContext<
if (!this.methods.visitStatsCommand) break;
return this.visitStatsCommand(parent, commandNode, input as any);
}
// TODO: uncomment this when the command is implemented
// case 'inline_stats': {
// if (!this.methods.visitInlineStatsCommand) break;
// return this.visitInlineStatsCommand(parent, commandNode, input as any);
// }
case 'inline_stats': {
if (!this.methods.visitInlineStatsCommand) break;
return this.visitInlineStatsCommand(parent, commandNode, input as any);
}
case 'lookup': {
if (!this.methods.visitLookupCommand) break;
return this.visitLookupCommand(parent, commandNode, input as any);

View file

@ -25,7 +25,7 @@ const aliasTable: Record<string, string[]> = {
const aliases = new Set(Object.values(aliasTable).flat());
const evalSupportedCommandsAndOptions = {
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
};

View file

@ -9,7 +9,7 @@
import { suggest } from './autocomplete';
import { evalFunctionDefinitions } from '../definitions/functions';
import { timeUnitsToSuggest } from '../definitions/literals';
import { commandDefinitions } from '../definitions/commands';
import { commandDefinitions as unmodifiedCommandDefinitions } from '../definitions/commands';
import {
getSafeInsertText,
getUnitDuration,
@ -45,6 +45,7 @@ const roundParameterTypes = ['double', 'integer', 'long', 'unsigned_long'] as co
const powParameterTypes = ['double', 'integer', 'long', 'unsigned_long'] as const;
const log10ParameterTypes = ['double', 'integer', 'long', 'unsigned_long'] as const;
const commandDefinitions = unmodifiedCommandDefinitions.filter(({ hidden }) => !hidden);
describe('autocomplete', () => {
type TestArgs = [
string,

View file

@ -246,6 +246,7 @@ export async function suggest(
if (!ast.length) {
return suggestions.filter(isSourceCommand);
}
return suggestions.filter((def) => !isSourceCommand(def));
}

View file

@ -82,9 +82,9 @@ export const getBuiltinCompatibleFunctionDefinition = (
.map(getSuggestionBuiltinDefinition);
};
export const commandAutocompleteDefinitions: SuggestionRawDefinition[] = getAllCommands().map(
getSuggestionCommandDefinition
);
export const commandAutocompleteDefinitions: SuggestionRawDefinition[] = getAllCommands()
.filter(({ hidden }) => !hidden)
.map(getSuggestionCommandDefinition);
function buildCharCompleteItem(
label: string,

View file

@ -31,7 +31,7 @@ function createNumericAggDefinition({
name,
type: 'agg',
description,
supportedCommands: ['stats', 'metrics'],
supportedCommands: ['stats', 'inlinestats', 'metrics'],
signatures: [
...ESQL_NUMBER_TYPES.map((numericType) => ({
params: [
@ -105,7 +105,7 @@ export const statsAggregationFunctionDefinitions: FunctionDefinition[] = [
}
),
type: 'agg',
supportedCommands: ['stats', 'metrics'],
supportedCommands: ['stats', 'inlinestats', 'metrics'],
signatures: [
...ESQL_COMMON_NUMERIC_TYPES.map((numericType: FunctionParameterType) => {
return ESQL_COMMON_NUMERIC_TYPES.map((weightType: FunctionParameterType) => ({
@ -133,7 +133,7 @@ export const statsAggregationFunctionDefinitions: FunctionDefinition[] = [
defaultMessage: 'Returns the maximum value in a field.',
}),
type: 'agg',
supportedCommands: ['stats', 'metrics'],
supportedCommands: ['stats', 'inlinestats', 'metrics'],
signatures: [
...ESQL_COMMON_NUMERIC_TYPES.map((type) => ({
params: [{ name: 'column', type, noNestingFunctions: true }],
@ -164,7 +164,7 @@ export const statsAggregationFunctionDefinitions: FunctionDefinition[] = [
defaultMessage: 'Returns the minimum value in a field.',
}),
type: 'agg',
supportedCommands: ['stats', 'metrics'],
supportedCommands: ['stats', 'inlinestats', 'metrics'],
signatures: [
...ESQL_COMMON_NUMERIC_TYPES.map((type) => ({
params: [{ name: 'column', type, noNestingFunctions: true }],
@ -197,7 +197,7 @@ export const statsAggregationFunctionDefinitions: FunctionDefinition[] = [
description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.countDoc', {
defaultMessage: 'Returns the count of the values in a field.',
}),
supportedCommands: ['stats', 'metrics'],
supportedCommands: ['stats', 'inlinestats', 'metrics'],
signatures: [
{
params: [
@ -223,7 +223,7 @@ export const statsAggregationFunctionDefinitions: FunctionDefinition[] = [
defaultMessage: 'Returns the count of distinct values in a field.',
}
),
supportedCommands: ['stats', 'metrics'],
supportedCommands: ['stats', 'inlinestats', 'metrics'],
signatures: [
{
params: [
@ -252,7 +252,7 @@ export const statsAggregationFunctionDefinitions: FunctionDefinition[] = [
defaultMessage: 'Returns the count of distinct values in a field.',
}
),
supportedCommands: ['stats', 'metrics'],
supportedCommands: ['stats', 'inlinestats', 'metrics'],
signatures: [
{
params: [{ name: 'column', type: 'cartesian_point', noNestingFunctions: true }],
@ -338,7 +338,7 @@ export const statsAggregationFunctionDefinitions: FunctionDefinition[] = [
'An aggregation that computes the weighted average of numeric values that are extracted from the aggregated documents.',
}
),
supportedCommands: ['stats', 'metrics'],
supportedCommands: ['stats', 'inlinestats', 'metrics'],
signatures: [
...ESQL_COMMON_NUMERIC_TYPES.map((numericType: FunctionParameterType) => {
return ESQL_COMMON_NUMERIC_TYPES.map((weightType: FunctionParameterType) => ({

View file

@ -591,7 +591,16 @@ const otherDefinitions: FunctionDefinition[] = [
description: i18n.translate('kbn-esql-validation-autocomplete.esql.definition.assignDoc', {
defaultMessage: 'Assign (=)',
}),
supportedCommands: ['eval', 'stats', 'metrics', 'row', 'dissect', 'where', 'enrich'],
supportedCommands: [
'eval',
'stats',
'inlinestats',
'metrics',
'row',
'dissect',
'where',
'enrich',
],
supportedOptions: ['by', 'with'],
signatures: [
{

View file

@ -32,6 +32,121 @@ import {
} from './options';
import type { CommandDefinition } from './types';
const statsValidator = (command: ESQLCommand) => {
const messages: ESQLMessage[] = [];
const commandName = command.name.toUpperCase();
if (!command.args.length) {
messages.push({
location: command.location,
text: i18n.translate('kbn-esql-validation-autocomplete.esql.validation.statsNoArguments', {
defaultMessage:
'At least one aggregation or grouping expression required in [{commandName}]',
values: { commandName },
}),
type: 'error',
code: 'statsNoArguments',
});
}
// now that all functions are supported, there's a specific check to perform
// unfortunately the logic here is a bit complex as it needs to dig deeper into the args
// until an agg function is detected
// in the long run this might be integrated into the validation function
const statsArg = command.args
.flatMap((arg) => (isAssignment(arg) ? arg.args[1] : arg))
.filter(isFunctionItem);
if (statsArg.length) {
function isAggFunction(arg: ESQLAstItem): arg is ESQLFunction {
return isFunctionItem(arg) && getFunctionDefinition(arg.name)?.type === 'agg';
}
function isOtherFunction(arg: ESQLAstItem): arg is ESQLFunction {
return isFunctionItem(arg) && getFunctionDefinition(arg.name)?.type !== 'agg';
}
function checkAggExistence(arg: ESQLFunction): boolean {
// TODO the grouping function check may not
// hold true for all future cases
if (isAggFunction(arg)) {
return true;
}
if (isOtherFunction(arg)) {
return (arg as ESQLFunction).args.filter(isFunctionItem).some(checkAggExistence);
}
return false;
}
// first check: is there an agg function somewhere?
const noAggsExpressions = statsArg.filter((arg) => !checkAggExistence(arg));
if (noAggsExpressions.length) {
messages.push(
...noAggsExpressions.map((fn) => ({
location: fn.location,
text: i18n.translate(
'kbn-esql-validation-autocomplete.esql.validation.statsNoAggFunction',
{
defaultMessage:
'At least one aggregation function required in [{commandName}], found [{expression}]',
values: {
expression: fn.text,
commandName,
},
}
),
type: 'error' as const,
code: 'statsNoAggFunction',
}))
);
} else {
function isConstantOrAggFn(arg: ESQLAstItem): boolean {
return isLiteralItem(arg) || isAggFunction(arg);
}
// now check that:
// * the agg function is at root level
// * or if it's a builtin function, then all operands are agg functions or literals
// * or if it's a eval function then all arguments are agg functions or literals
function checkFunctionContent(arg: ESQLFunction) {
// TODO the grouping function check may not
// hold true for all future cases
if (isAggFunction(arg)) {
return true;
}
return (arg as ESQLFunction).args.every(
(subArg): boolean =>
isConstantOrAggFn(subArg) ||
(isOtherFunction(subArg) ? checkFunctionContent(subArg) : false)
);
}
// @TODO: improve here the check to get the last instance of the invalidExpression
// to provide a better location for the error message
// i.e. STATS round(round(round( a + sum(b) )))
// should return the location of the + node, just before the agg one
const invalidExpressions = statsArg.filter((arg) => !checkFunctionContent(arg));
if (invalidExpressions.length) {
messages.push(
...invalidExpressions.map((fn) => ({
location: fn.location,
text: i18n.translate(
'kbn-esql-validation-autocomplete.esql.validation.noCombinationOfAggAndNonAggValues',
{
defaultMessage:
'Cannot combine aggregation and non-aggregation values in [{commandName}], found [{expression}]',
values: {
expression: fn.text,
commandName,
},
}
),
type: 'error' as const,
code: 'statsNoCombinationOfAggAndNonAggValues',
}))
);
}
}
}
return messages;
};
export const commandDefinitions: CommandDefinition[] = [
{
name: 'row',
@ -118,120 +233,29 @@ export const commandDefinitions: CommandDefinition[] = [
},
options: [byOption],
modes: [],
validate: (command: ESQLCommand) => {
const messages: ESQLMessage[] = [];
if (!command.args.length) {
messages.push({
location: command.location,
text: i18n.translate(
'kbn-esql-validation-autocomplete.esql.validation.statsNoArguments',
{
defaultMessage: 'At least one aggregation or grouping expression required in [STATS]',
}
),
type: 'error',
code: 'statsNoArguments',
});
}
// now that all functions are supported, there's a specific check to perform
// unfortunately the logic here is a bit complex as it needs to dig deeper into the args
// until an agg function is detected
// in the long run this might be integrated into the validation function
const statsArg = command.args
.flatMap((arg) => (isAssignment(arg) ? arg.args[1] : arg))
.filter(isFunctionItem);
if (statsArg.length) {
function isAggFunction(arg: ESQLAstItem): arg is ESQLFunction {
return isFunctionItem(arg) && getFunctionDefinition(arg.name)?.type === 'agg';
}
function isOtherFunction(arg: ESQLAstItem): arg is ESQLFunction {
return isFunctionItem(arg) && getFunctionDefinition(arg.name)?.type !== 'agg';
}
function checkAggExistence(arg: ESQLFunction): boolean {
// TODO the grouping function check may not
// hold true for all future cases
if (isAggFunction(arg)) {
return true;
}
if (isOtherFunction(arg)) {
return (arg as ESQLFunction).args.filter(isFunctionItem).some(checkAggExistence);
}
return false;
}
// first check: is there an agg function somewhere?
const noAggsExpressions = statsArg.filter((arg) => !checkAggExistence(arg));
if (noAggsExpressions.length) {
messages.push(
...noAggsExpressions.map((fn) => ({
location: fn.location,
text: i18n.translate(
'kbn-esql-validation-autocomplete.esql.validation.statsNoAggFunction',
{
defaultMessage:
'At least one aggregation function required in [STATS], found [{expression}]',
values: {
expression: fn.text,
},
}
),
type: 'error' as const,
code: 'statsNoAggFunction',
}))
);
} else {
function isConstantOrAggFn(arg: ESQLAstItem): boolean {
return isLiteralItem(arg) || isAggFunction(arg);
}
// now check that:
// * the agg function is at root level
// * or if it's a builtin function, then all operands are agg functions or literals
// * or if it's a eval function then all arguments are agg functions or literals
function checkFunctionContent(arg: ESQLFunction) {
// TODO the grouping function check may not
// hold true for all future cases
if (isAggFunction(arg)) {
return true;
}
return (arg as ESQLFunction).args.every(
(subArg): boolean =>
isConstantOrAggFn(subArg) ||
(isOtherFunction(subArg) ? checkFunctionContent(subArg) : false)
);
}
// @TODO: improve here the check to get the last instance of the invalidExpression
// to provide a better location for the error message
// i.e. STATS round(round(round( a + sum(b) )))
// should return the location of the + node, just before the agg one
const invalidExpressions = statsArg.filter((arg) => !checkFunctionContent(arg));
if (invalidExpressions.length) {
messages.push(
...invalidExpressions.map((fn) => ({
location: fn.location,
text: i18n.translate(
'kbn-esql-validation-autocomplete.esql.validation.noCombinationOfAggAndNonAggValues',
{
defaultMessage:
'Cannot combine aggregation and non-aggregation values in [STATS], found [{expression}]',
values: {
expression: fn.text,
},
}
),
type: 'error' as const,
code: 'statsNoCombinationOfAggAndNonAggValues',
}))
);
}
}
}
return messages;
},
validate: statsValidator,
},
{
name: 'inlinestats',
hidden: true,
description: i18n.translate(
'kbn-esql-validation-autocomplete.esql.definitions.inlineStatsDoc',
{
defaultMessage:
'Calculates an aggregate result and merges that result back into the stream of input data. Without the optional `BY` clause this will produce a single result which is appended to each row. With a `BY` clause this will produce one result per grouping and merge the result into the stream based on matching group keys.',
}
),
examples: ['… | EVAL bar = a * b | INLINESTATS m = MAX(bar) BY b'],
signature: {
multipleParams: true,
params: [{ name: 'expression', type: 'function', optional: true }],
},
options: [byOption],
modes: [],
// Reusing the same validation logic as stats command
validate: statsValidator,
},
{
name: 'eval',
description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.evalDoc', {

View file

@ -80,7 +80,7 @@ const absDefinition: FunctionDefinition = {
returnType: 'unsigned_long',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -139,7 +139,7 @@ const acosDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a=.9\n| EVAL acos=ACOS(a)'],
@ -196,7 +196,7 @@ const asinDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a=.9\n| EVAL asin=ASIN(a)'],
@ -253,7 +253,7 @@ const atanDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a=12.9\n| EVAL atan=ATAN(a)'],
@ -510,7 +510,7 @@ const atan2Definition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW y=12.9, x=.6\n| EVAL atan2=ATAN2(y, x)'],
@ -567,7 +567,7 @@ const cbrtDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW d = 1000.0\n| EVAL c = cbrt(d)'],
@ -623,7 +623,7 @@ const ceilDefinition: FunctionDefinition = {
returnType: 'unsigned_long',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a=1.8\n| EVAL a=CEIL(a)'],
@ -672,7 +672,7 @@ const cidrMatchDefinition: FunctionDefinition = {
minParams: 2,
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -938,7 +938,7 @@ const coalesceDefinition: FunctionDefinition = {
minParams: 1,
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a=null, b="b"\n| EVAL COALESCE(a, b)'],
@ -1018,7 +1018,7 @@ const concatDefinition: FunctionDefinition = {
minParams: 2,
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -1076,7 +1076,7 @@ const cosDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a=1.8 \n| EVAL cos=COS(a)'],
@ -1132,7 +1132,7 @@ const coshDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a=1.8 \n| EVAL cosh=COSH(a)'],
@ -1250,7 +1250,7 @@ const dateDiffDefinition: FunctionDefinition = {
returnType: 'integer',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -1330,7 +1330,7 @@ const dateExtractDefinition: FunctionDefinition = {
returnType: 'long',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -1379,7 +1379,7 @@ const dateFormatDefinition: FunctionDefinition = {
returnType: 'keyword',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -1458,7 +1458,7 @@ const dateParseDefinition: FunctionDefinition = {
returnType: 'date',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW date_string = "2022-05-06"\n| EVAL date = DATE_PARSE("yyyy-MM-dd", date_string)'],
@ -1504,7 +1504,7 @@ const dateTruncDefinition: FunctionDefinition = {
returnType: 'date',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -1528,7 +1528,7 @@ const eDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW E()'],
@ -1605,7 +1605,7 @@ const endsWithDefinition: FunctionDefinition = {
returnType: 'boolean',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['FROM employees\n| KEEP last_name\n| EVAL ln_E = ENDS_WITH(last_name, "d")'],
@ -1661,7 +1661,7 @@ const expDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW d = 5.0\n| EVAL s = EXP(d)'],
@ -1717,7 +1717,7 @@ const floorDefinition: FunctionDefinition = {
returnType: 'unsigned_long',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a=1.8\n| EVAL a=FLOOR(a)'],
@ -1753,7 +1753,7 @@ const fromBase64Definition: FunctionDefinition = {
returnType: 'keyword',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['row a = "ZWxhc3RpYw==" \n| eval d = from_base64(a)'],
@ -1953,7 +1953,7 @@ const greatestDefinition: FunctionDefinition = {
minParams: 1,
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a = 10, b = 20\n| EVAL g = GREATEST(a, b)'],
@ -1989,7 +1989,7 @@ const ipPrefixDefinition: FunctionDefinition = {
returnType: 'ip',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -2191,7 +2191,7 @@ const leastDefinition: FunctionDefinition = {
minParams: 1,
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a = 10, b = 20\n| EVAL l = LEAST(a, b)'],
@ -2238,7 +2238,7 @@ const leftDefinition: FunctionDefinition = {
returnType: 'keyword',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -2276,7 +2276,7 @@ const lengthDefinition: FunctionDefinition = {
returnType: 'integer',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['FROM employees\n| KEEP first_name, last_name\n| EVAL fn_length = LENGTH(first_name)'],
@ -2433,7 +2433,7 @@ const locateDefinition: FunctionDefinition = {
returnType: 'integer',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['row a = "hello"\n| eval a_ll = locate(a, "ll")'],
@ -2730,14 +2730,14 @@ const logDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: (fnDef: ESQLFunction) => {
const messages = [];
// do not really care here about the base and field
// just need to check both values are not negative
for (const arg of fnDef.args) {
if (isLiteralItem(arg) && Number(arg.value) < 0) {
if (isLiteralItem(arg) && typeof arg.value === 'number' && arg.value < 0) {
messages.push({
type: 'warning' as const,
code: 'logOfNegativeValue',
@ -2813,14 +2813,14 @@ const log10Definition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: (fnDef: ESQLFunction) => {
const messages = [];
// do not really care here about the base and field
// just need to check both values are not negative
for (const arg of fnDef.args) {
if (isLiteralItem(arg) && Number(arg.value) < 0) {
if (isLiteralItem(arg) && typeof arg.value === 'number' && arg.value < 0) {
messages.push({
type: 'warning' as const,
code: 'logOfNegativeValue',
@ -2872,7 +2872,7 @@ const ltrimDefinition: FunctionDefinition = {
returnType: 'text',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -3085,7 +3085,7 @@ const mvAppendDefinition: FunctionDefinition = {
returnType: 'version',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [],
@ -3142,7 +3142,7 @@ const mvAvgDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a=[3, 5, 1, 6]\n| EVAL avg_a = MV_AVG(a)'],
@ -3219,7 +3219,7 @@ const mvConcatDefinition: FunctionDefinition = {
returnType: 'keyword',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -3379,7 +3379,7 @@ const mvCountDefinition: FunctionDefinition = {
returnType: 'integer',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a=["foo", "zoo", "bar"]\n| EVAL count_a = MV_COUNT(a)'],
@ -3525,7 +3525,7 @@ const mvDedupeDefinition: FunctionDefinition = {
returnType: 'version',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a=["foo", "foo", "bar", "foo"]\n| EVAL dedupe_a = MV_DEDUPE(a)'],
@ -3682,7 +3682,7 @@ const mvFirstDefinition: FunctionDefinition = {
returnType: 'version',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a="foo;bar;baz"\n| EVAL first_a = MV_FIRST(SPLIT(a, ";"))'],
@ -3839,7 +3839,7 @@ const mvLastDefinition: FunctionDefinition = {
returnType: 'version',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a="foo;bar;baz"\n| EVAL last_a = MV_LAST(SPLIT(a, ";"))'],
@ -3956,7 +3956,7 @@ const mvMaxDefinition: FunctionDefinition = {
returnType: 'version',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -4016,7 +4016,7 @@ const mvMedianDefinition: FunctionDefinition = {
returnType: 'unsigned_long',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -4136,7 +4136,7 @@ const mvMinDefinition: FunctionDefinition = {
returnType: 'version',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -4453,7 +4453,7 @@ const mvSliceDefinition: FunctionDefinition = {
returnType: 'version',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -4616,9 +4616,10 @@ const mvSortDefinition: FunctionDefinition = {
returnType: 'version',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a = [4, 2, -3, 2]\n| EVAL sa = mv_sort(a), sd = mv_sort(a, "DESC")'],
};
@ -4673,7 +4674,7 @@ const mvSumDefinition: FunctionDefinition = {
returnType: 'unsigned_long',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a=[3, 5, 6]\n| EVAL sum_a = MV_SUM(a)'],
@ -4910,7 +4911,7 @@ const mvZipDefinition: FunctionDefinition = {
returnType: 'keyword',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -4932,7 +4933,7 @@ const nowDefinition: FunctionDefinition = {
returnType: 'date',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW current_date = NOW()', 'FROM sample_data\n| WHERE @timestamp > NOW() - 1 hour'],
@ -4952,7 +4953,7 @@ const piDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW PI()'],
@ -5208,7 +5209,7 @@ const powDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -5258,7 +5259,7 @@ const repeatDefinition: FunctionDefinition = {
returnType: 'keyword',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a = "Hello!"\n| EVAL triple_a = REPEAT(a, 3);'],
@ -5435,7 +5436,7 @@ const replaceDefinition: FunctionDefinition = {
returnType: 'keyword',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW str = "Hello World"\n| EVAL str = REPLACE(str, "World", "Universe")\n| KEEP str'],
@ -5482,7 +5483,7 @@ const rightDefinition: FunctionDefinition = {
returnType: 'keyword',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -5586,7 +5587,7 @@ const roundDefinition: FunctionDefinition = {
returnType: 'unsigned_long',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -5624,7 +5625,7 @@ const rtrimDefinition: FunctionDefinition = {
returnType: 'text',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -5683,7 +5684,7 @@ const signumDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW d = 100.0\n| EVAL s = SIGNUM(d)'],
@ -5739,7 +5740,7 @@ const sinDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a=1.8 \n| EVAL sin=SIN(a)'],
@ -5795,7 +5796,7 @@ const sinhDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a=1.8 \n| EVAL sinh=SINH(a)'],
@ -5871,7 +5872,7 @@ const splitDefinition: FunctionDefinition = {
returnType: 'keyword',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW words="foo;bar;baz;qux;quux;corge"\n| EVAL word = SPLIT(words, ";")'],
@ -5928,7 +5929,7 @@ const sqrtDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW d = 100.0\n| EVAL s = SQRT(d)'],
@ -6065,7 +6066,7 @@ const stContainsDefinition: FunctionDefinition = {
returnType: 'boolean',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -6204,7 +6205,7 @@ const stDisjointDefinition: FunctionDefinition = {
returnType: 'boolean',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -6253,7 +6254,7 @@ const stDistanceDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -6392,7 +6393,7 @@ const stIntersectsDefinition: FunctionDefinition = {
returnType: 'boolean',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -6531,7 +6532,7 @@ const stWithinDefinition: FunctionDefinition = {
returnType: 'boolean',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -6570,7 +6571,7 @@ const stXDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -6609,7 +6610,7 @@ const stYDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -6688,7 +6689,7 @@ const startsWithDefinition: FunctionDefinition = {
returnType: 'boolean',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['FROM employees\n| KEEP last_name\n| EVAL ln_S = STARTS_WITH(last_name, "B")'],
@ -6745,7 +6746,7 @@ const substringDefinition: FunctionDefinition = {
returnType: 'keyword',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -6805,7 +6806,7 @@ const tanDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a=1.8 \n| EVAL tan=TAN(a)'],
@ -6861,7 +6862,7 @@ const tanhDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a=1.8 \n| EVAL tanh=TANH(a)'],
@ -6881,7 +6882,7 @@ const tauDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW TAU()'],
@ -6917,7 +6918,7 @@ const toBase64Definition: FunctionDefinition = {
returnType: 'keyword',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['row a = "elastic" \n| eval e = to_base64(a)'],
@ -7004,7 +7005,7 @@ const toBooleanDefinition: FunctionDefinition = {
returnType: 'boolean',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW str = ["true", "TRuE", "false", "", "yes", "1"]\n| EVAL bool = TO_BOOLEAN(str)'],
@ -7054,7 +7055,7 @@ const toCartesianpointDefinition: FunctionDefinition = {
returnType: 'cartesian_point',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -7116,7 +7117,7 @@ const toCartesianshapeDefinition: FunctionDefinition = {
returnType: 'cartesian_shape',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -7205,7 +7206,7 @@ const toDatetimeDefinition: FunctionDefinition = {
returnType: 'date',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -7264,7 +7265,7 @@ const toDegreesDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW rad = [1.57, 3.14, 4.71]\n| EVAL deg = TO_DEGREES(rad)'],
@ -7391,7 +7392,7 @@ const toDoubleDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -7440,7 +7441,7 @@ const toGeopointDefinition: FunctionDefinition = {
returnType: 'geo_point',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW wkt = "POINT(42.97109630194 14.7552534413725)"\n| EVAL pt = TO_GEOPOINT(wkt)'],
@ -7497,7 +7498,7 @@ const toGeoshapeDefinition: FunctionDefinition = {
returnType: 'geo_shape',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -7606,7 +7607,7 @@ const toIntegerDefinition: FunctionDefinition = {
returnType: 'integer',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW long = [5013792, 2147483647, 501379200000]\n| EVAL int = TO_INTEGER(long)'],
@ -7652,7 +7653,7 @@ const toIpDefinition: FunctionDefinition = {
returnType: 'ip',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -7771,7 +7772,7 @@ const toLongDefinition: FunctionDefinition = {
returnType: 'long',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -7809,7 +7810,7 @@ const toLowerDefinition: FunctionDefinition = {
returnType: 'text',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW message = "Some Text"\n| EVAL message_lower = TO_LOWER(message)'],
@ -7865,7 +7866,7 @@ const toRadiansDefinition: FunctionDefinition = {
returnType: 'double',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW deg = [90.0, 180.0, 270.0]\n| EVAL rad = TO_RADIANS(deg)'],
@ -8021,7 +8022,7 @@ const toStringDefinition: FunctionDefinition = {
returnType: 'keyword',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW a=10\n| EVAL j = TO_STRING(a)', 'ROW a=[10, 9, 8]\n| EVAL j = TO_STRING(a)'],
@ -8121,7 +8122,7 @@ const toUnsignedLongDefinition: FunctionDefinition = {
returnType: 'unsigned_long',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -8159,7 +8160,7 @@ const toUpperDefinition: FunctionDefinition = {
returnType: 'text',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW message = "Some Text"\n| EVAL message_upper = TO_UPPER(message)'],
@ -8205,7 +8206,7 @@ const toVersionDefinition: FunctionDefinition = {
returnType: 'version',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: ['ROW v = TO_VERSION("1.2.3")'],
@ -8241,7 +8242,7 @@ const trimDefinition: FunctionDefinition = {
returnType: 'text',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [
@ -8274,7 +8275,7 @@ const caseDefinition: FunctionDefinition = {
returnType: 'any',
},
],
supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'],
supportedOptions: ['by'],
validate: undefined,
examples: [

View file

@ -147,6 +147,10 @@ export interface CommandBaseDefinition {
name: string;
alias?: string;
description: string;
/**
* Whether to show or hide in autocomplete suggestion list
*/
hidden?: boolean;
signature: {
multipleParams: boolean;
// innerType here is useful to drill down the type in case of "column"

View file

@ -156,9 +156,9 @@ export function collectVariables(
): Map<string, ESQLVariable[]> {
const variables = new Map<string, ESQLVariable[]>();
for (const command of commands) {
if (['row', 'eval', 'stats', 'metrics'].includes(command.name)) {
if (['row', 'eval', 'stats', 'inlinestats', 'metrics'].includes(command.name)) {
collectVariablesFromList(command.args, fields, queryString, variables);
if (command.name === 'stats') {
if (command.name === 'stats' || command.name === 'inlinestats') {
const commandOptionsWithAssignment = command.args.filter(
(arg) => isOptionItem(arg) && arg.name === 'by'
) as ESQLCommandOption[];

View file

@ -0,0 +1,386 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import * as helpers from '../helpers';
export const validationStatsCommandTestSuite = (setup: helpers.Setup) => {
describe('validation', () => {
describe('command', () => {
describe('INLINESTATS <aggregates> [ BY <grouping> ]', () => {
test('no errors on correct usage', async () => {
const { expectErrors } = await setup();
await expectErrors('from a_index | INLINESTATS by textField', []);
await expectErrors(
`FROM index
| EVAL doubleField * 3.281
| INLINESTATS avg_doubleField = AVG(\`doubleField * 3.281\`)`,
[]
);
await expectErrors(
`FROM index | INLINESTATS AVG(doubleField) by round(doubleField) + 1 | EVAL \`round(doubleField) + 1\` / 2`,
[]
);
});
test('errors on invalid command start', async () => {
const { expectErrors } = await setup();
await expectErrors('from a_index | INLINESTATS ', [
'At least one aggregation or grouping expression required in [INLINESTATS]',
]);
});
describe('... <aggregates> ...', () => {
test('no errors on correct usage', async () => {
const { expectErrors } = await setup();
await expectErrors('from a_index | INLINESTATS avg(doubleField) by 1', []);
await expectErrors('from a_index | INLINESTATS count(`doubleField`)', []);
await expectErrors('from a_index | INLINESTATS count(*)', []);
await expectErrors('from a_index | INLINESTATS count()', []);
await expectErrors('from a_index | INLINESTATS var0 = count(*)', []);
await expectErrors('from a_index | INLINESTATS var0 = count()', []);
await expectErrors('from a_index | INLINESTATS var0 = avg(doubleField), count(*)', []);
await expectErrors(`from a_index | INLINESTATS sum(case(false, 0, 1))`, []);
await expectErrors(`from a_index | INLINESTATS var0 = sum( case(false, 0, 1))`, []);
// "or" must accept "null"
await expectErrors('from a_index | INLINESTATS count(textField == "a" or null)', []);
});
test('sub-command can reference aggregated field', async () => {
const { expectErrors } = await setup();
for (const subCommand of ['keep', 'drop', 'eval']) {
await expectErrors(
'from a_index | INLINESTATS count(`doubleField`) | ' +
subCommand +
' `count(``doubleField``)` ',
[]
);
}
});
test('errors on agg and non-agg mix', async () => {
const { expectErrors } = await setup();
await expectErrors(
'from a_index | INLINESTATS sum( doubleField ) + abs( doubleField ) ',
[
'Cannot combine aggregation and non-aggregation values in [INLINESTATS], found [sum(doubleField)+abs(doubleField)]',
]
);
await expectErrors(
'from a_index | INLINESTATS abs( doubleField + sum( doubleField )) ',
[
'Cannot combine aggregation and non-aggregation values in [INLINESTATS], found [abs(doubleField+sum(doubleField))]',
]
);
});
test('errors on each aggregation field, which does not contain at least one agg function', async () => {
const { expectErrors } = await setup();
await expectErrors('from a_index | INLINESTATS doubleField + 1', [
'At least one aggregation function required in [INLINESTATS], found [doubleField+1]',
]);
await expectErrors('from a_index | INLINESTATS doubleField + 1, textField', [
'At least one aggregation function required in [INLINESTATS], found [doubleField+1]',
'Expected an aggregate function or group but got [textField] of type [FieldAttribute]',
]);
await expectErrors(
'from a_index | INLINESTATS doubleField + 1, doubleField + 2, count()',
[
'At least one aggregation function required in [INLINESTATS], found [doubleField+1]',
'At least one aggregation function required in [INLINESTATS], found [doubleField+2]',
]
);
await expectErrors(
'from a_index | INLINESTATS doubleField + 1, doubleField + count(), count()',
['At least one aggregation function required in [INLINESTATS], found [doubleField+1]']
);
await expectErrors('from a_index | INLINESTATS 5 + doubleField + 1', [
'At least one aggregation function required in [INLINESTATS], found [5+doubleField+1]',
]);
await expectErrors('from a_index | INLINESTATS doubleField + 1 by ipField', [
'At least one aggregation function required in [INLINESTATS], found [doubleField+1]',
]);
});
test('errors when input is not an aggregate function', async () => {
const { expectErrors } = await setup();
await expectErrors('from a_index | INLINESTATS doubleField ', [
'Expected an aggregate function or group but got [doubleField] of type [FieldAttribute]',
]);
});
test('various errors', async () => {
const { expectErrors } = await setup();
await expectErrors('from a_index | INLINESTATS doubleField=', [
"SyntaxError: mismatched input '<EOF>' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}",
]);
await expectErrors('from a_index | INLINESTATS doubleField=5 by ', [
"SyntaxError: mismatched input '<EOF>' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}",
]);
await expectErrors('from a_index | INLINESTATS avg(doubleField) by wrongField', [
'Unknown column [wrongField]',
]);
await expectErrors('from a_index | INLINESTATS avg(doubleField) by wrongField + 1', [
'Unknown column [wrongField]',
]);
await expectErrors(
'from a_index | INLINESTATS avg(doubleField) by var0 = wrongField + 1',
['Unknown column [wrongField]']
);
await expectErrors('from a_index | INLINESTATS var0 = avg(fn(number)), count(*)', [
'Unknown function [fn]',
]);
});
test('semantic errors', async () => {
const { expectErrors } = await setup();
await expectErrors('from a_index | INLINESTATS count(round(*))', [
'Using wildcards (*) in round is not allowed',
]);
await expectErrors('from a_index | INLINESTATS count(count(*))', [
`Aggregate function's parameters must be an attribute, literal or a non-aggregation function; found [count(*)] of type [long]`,
]);
});
});
describe('... BY <grouping>', () => {
test('no errors on correct usage', async () => {
const { expectErrors } = await setup();
await expectErrors(
'from a_index | INLINESTATS avg(doubleField), percentile(doubleField, 50) by ipField',
[]
);
await expectErrors(
'from a_index | INLINESTATS avg(doubleField), percentile(doubleField, 50) BY ipField',
[]
);
await expectErrors(
'from a_index | INLINESTATS avg(doubleField), percentile(doubleField, 50) + 1 by ipField',
[]
);
for (const op of ['+', '-', '*', '/', '%']) {
await expectErrors(
`from a_index | INLINESTATS avg(doubleField) ${op} percentile(doubleField, 50) BY ipField`,
[]
);
}
});
test('cannot specify <grouping> without <aggregates>', async () => {
const { expectErrors } = await setup();
await expectErrors('from a_index | INLINESTATS by ', [
"SyntaxError: mismatched input '<EOF>' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}",
]);
});
test('syntax errors in <aggregates>', async () => {
const { expectErrors } = await setup();
await expectErrors('from a_index | INLINESTATS count(* + 1) BY ipField', [
"SyntaxError: no viable alternative at input 'count(* +'",
]);
await expectErrors(
'from a_index | INLINESTATS count(* + round(doubleField)) BY ipField',
["SyntaxError: no viable alternative at input 'count(* +'"]
);
});
test('semantic errors in <aggregates>', async () => {
const { expectErrors } = await setup();
await expectErrors('from a_index | INLINESTATS count(round(*)) BY ipField', [
'Using wildcards (*) in round is not allowed',
]);
await expectErrors('from a_index | INLINESTATS count(count(*)) BY ipField', [
`Aggregate function's parameters must be an attribute, literal or a non-aggregation function; found [count(*)] of type [long]`,
]);
});
test('various errors', async () => {
const { expectErrors } = await setup();
await expectErrors(
'from a_index | INLINESTATS avg(doubleField) by percentile(doubleField)',
['INLINESTATS BY does not support function percentile']
);
await expectErrors(
'from a_index | INLINESTATS avg(doubleField) by textField, percentile(doubleField) by ipField',
[
"SyntaxError: mismatched input 'by' expecting <EOF>",
'INLINESTATS BY does not support function percentile',
]
);
});
describe('constant-only parameters', () => {
test('no errors', async () => {
const { expectErrors } = await setup();
await expectErrors(
'from index | INLINESTATS by bucket(dateField, 1 + 30 / 10, "", "")',
[]
);
await expectErrors(
'from index | INLINESTATS by bucket(dateField, 1 + 30 / 10, concat("", ""), "")',
['Argument of [bucket] must be [date], found value [concat("","")] type [keyword]']
);
});
test('errors', async () => {
const { expectErrors } = await setup();
await expectErrors('from index | INLINESTATS by bucket(dateField, pi(), "", "")', [
'Argument of [bucket] must be [integer], found value [pi()] type [double]',
]);
await expectErrors(
'from index | INLINESTATS by bucket(dateField, abs(doubleField), "", "")',
['Argument of [bucket] must be a constant, received [abs(doubleField)]']
);
await expectErrors(
'from index | INLINESTATS by bucket(dateField, abs(length(doubleField)), "", "")',
['Argument of [bucket] must be a constant, received [abs(length(doubleField))]']
);
await expectErrors(
'from index | INLINESTATS by bucket(dateField, doubleField, textField, textField)',
[
'Argument of [bucket] must be a constant, received [doubleField]',
'Argument of [bucket] must be a constant, received [textField]',
'Argument of [bucket] must be a constant, received [textField]',
]
);
});
});
});
describe('nesting', () => {
const NESTING_LEVELS = 4;
const NESTED_DEPTHS = Array(NESTING_LEVELS)
.fill(0)
.map((_, i) => i + 1);
for (const nesting of NESTED_DEPTHS) {
describe(`depth = ${nesting}`, () => {
describe('builtin', () => {
const builtinWrapping = Array(nesting).fill('+1').join('');
test('no errors', async () => {
const { expectErrors } = await setup();
await expectErrors(
`from a_index | INLINESTATS 5 + avg(doubleField) ${builtinWrapping}`,
[]
);
await expectErrors(
`from a_index | INLINESTATS 5 ${builtinWrapping} + avg(doubleField)`,
[]
);
});
test('errors', async () => {
const { expectErrors } = await setup();
await expectErrors(
`from a_index | INLINESTATS 5 ${builtinWrapping} + doubleField`,
[
`At least one aggregation function required in [INLINESTATS], found [5${builtinWrapping}+doubleField]`,
]
);
await expectErrors(
`from a_index | INLINESTATS 5 + doubleField ${builtinWrapping}`,
[
`At least one aggregation function required in [INLINESTATS], found [5+doubleField${builtinWrapping}]`,
]
);
await expectErrors(
`from a_index | INLINESTATS 5 + doubleField ${builtinWrapping}, var0 = sum(doubleField)`,
[
`At least one aggregation function required in [INLINESTATS], found [5+doubleField${builtinWrapping}]`,
]
);
});
});
describe('EVAL', () => {
const evalWrapping = Array(nesting).fill('round(').join('');
const closingWrapping = Array(nesting).fill(')').join('');
test('no errors', async () => {
const { expectErrors } = await setup();
await expectErrors(
`from a_index | INLINESTATS ${evalWrapping} sum(doubleField) ${closingWrapping}`,
[]
);
await expectErrors(
`from a_index | INLINESTATS ${evalWrapping} sum(doubleField) ${closingWrapping} + ${evalWrapping} sum(doubleField) ${closingWrapping}`,
[]
);
await expectErrors(
`from a_index | INLINESTATS ${evalWrapping} sum(doubleField + doubleField) ${closingWrapping}`,
[]
);
await expectErrors(
`from a_index | INLINESTATS ${evalWrapping} sum(doubleField + round(doubleField)) ${closingWrapping}`,
[]
);
await expectErrors(
`from a_index | INLINESTATS ${evalWrapping} sum(doubleField + round(doubleField)) ${closingWrapping} + ${evalWrapping} sum(doubleField + round(doubleField)) ${closingWrapping}`,
[]
);
await expectErrors(
`from a_index | INLINESTATS sum(${evalWrapping} doubleField ${closingWrapping} )`,
[]
);
await expectErrors(
`from a_index | INLINESTATS sum(${evalWrapping} doubleField ${closingWrapping} ) + sum(${evalWrapping} doubleField ${closingWrapping} )`,
[]
);
});
test('errors', async () => {
const { expectErrors } = await setup();
await expectErrors(
`from a_index | INLINESTATS ${evalWrapping} doubleField + sum(doubleField) ${closingWrapping}`,
[
`Cannot combine aggregation and non-aggregation values in [INLINESTATS], found [${evalWrapping}doubleField+sum(doubleField)${closingWrapping}]`,
]
);
await expectErrors(
`from a_index | INLINESTATS ${evalWrapping} doubleField + sum(doubleField) ${closingWrapping}, var0 = sum(doubleField)`,
[
`Cannot combine aggregation and non-aggregation values in [INLINESTATS], found [${evalWrapping}doubleField+sum(doubleField)${closingWrapping}]`,
]
);
await expectErrors(
`from a_index | INLINESTATS var0 = ${evalWrapping} doubleField + sum(doubleField) ${closingWrapping}, var1 = sum(doubleField)`,
[
`Cannot combine aggregation and non-aggregation values in [INLINESTATS], found [${evalWrapping}doubleField+sum(doubleField)${closingWrapping}]`,
]
);
});
});
});
}
});
});
});
});
};

View file

@ -0,0 +1,12 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import * as helpers from './helpers';
import { validationStatsCommandTestSuite } from './test_suites/validation.command.stats';
validationStatsCommandTestSuite(helpers.setup);

View file

@ -464,7 +464,7 @@ function validateFunction(
allMatchingArgDefinitionsAreConstantOnly || forceConstantOnly,
// use the nesting flag for now just for stats and metrics
// TODO: revisit this part later on to make it more generic
parentCommand === 'stats' || parentCommand === 'metrics'
['stats', 'inlinestats', 'metrics'].includes(parentCommand)
? isNested || !isAssignment(astFunction)
: false
);
@ -1010,7 +1010,7 @@ function validateCommand(command: ESQLCommand, references: ReferenceMaps): ESQLM
);
}
if (isColumnItem(arg)) {
if (command.name === 'stats') {
if (command.name === 'stats' || command.name === 'inlinestats') {
messages.push(errors.unknownAggFunction(arg));
} else {
messages.push(...validateColumnForCommand(arg, command.name, references));

View file

@ -60,6 +60,7 @@ export const buildESQlTheme = (): monaco.editor.IStandaloneThemeData => ({
'metadata',
'mv_expand',
'stats',
'inlinestats',
'dissect',
'grok',
'project',

View file

@ -5164,10 +5164,7 @@
"kbn-esql-validation-autocomplete.esql.validation.expectedConstantValue": "L'argument de [{fn}] doit être une constante, reçu [{given}]",
"kbn-esql-validation-autocomplete.esql.validation.metadataBracketsDeprecation": "Les crochets \"[]\" doivent être supprimés de la déclaration FROM METADATA",
"kbn-esql-validation-autocomplete.esql.validation.missingFunction": "Fonction inconnue [{name}]",
"kbn-esql-validation-autocomplete.esql.validation.noCombinationOfAggAndNonAggValues": "Impossible de combiner les valeurs agrégées et non agrégées dans [STATS], [{expression}] trouvé",
"kbn-esql-validation-autocomplete.esql.validation.noNestedArgumentSupport": "Les paramètres de la fonction agrégée doivent être un attribut, un littéral ou une fonction non agrégée ; trouvé [{name}] de type [{argType}]",
"kbn-esql-validation-autocomplete.esql.validation.statsNoAggFunction": "Au moins une fonction d'agrégation requise dans [STATS], [{expression}] trouvé",
"kbn-esql-validation-autocomplete.esql.validation.statsNoArguments": "[STATS] doit contenir au moins une expression d'agrégation ou de regroupement",
"kbn-esql-validation-autocomplete.esql.validation.typeOverwrite": "La colonne [{field}] de type {fieldType} a été écrasée par un nouveau type : {newType}",
"kbn-esql-validation-autocomplete.esql.validation.unknowAggregateFunction": "Attendait une fonction ou un groupe agrégé mais a obtenu [{value}] de type [{type}]",
"kbn-esql-validation-autocomplete.esql.validation.unknownColumn": "Colonne inconnue[{name}]",

View file

@ -5145,10 +5145,7 @@
"kbn-esql-validation-autocomplete.esql.validation.expectedConstantValue": "[{fn}]の引数は定数でなければなりません。[{given}]が渡されました",
"kbn-esql-validation-autocomplete.esql.validation.metadataBracketsDeprecation": "FROM METADATA宣言から角括弧[]を削除する必要があります",
"kbn-esql-validation-autocomplete.esql.validation.missingFunction": "不明な関数[{name}]",
"kbn-esql-validation-autocomplete.esql.validation.noCombinationOfAggAndNonAggValues": "[STATS]では集計値と非集計値を結合できません。[{expression}]が見つかりました",
"kbn-esql-validation-autocomplete.esql.validation.noNestedArgumentSupport": "集計関数のパラメーターは属性、リテラル、または非集計関数でなければなりません。タイプ[{argType}]の[{name}]が見つかりました",
"kbn-esql-validation-autocomplete.esql.validation.statsNoAggFunction": "[STATS]では1つ以上の集計関数が必要です。[{expression}]が見つかりました",
"kbn-esql-validation-autocomplete.esql.validation.statsNoArguments": "[STATS]では1つ以上の集計またはグループ式が必要です",
"kbn-esql-validation-autocomplete.esql.validation.typeOverwrite": "{fieldType}型の列[{field}]が新しい型の{newType}として上書きされました",
"kbn-esql-validation-autocomplete.esql.validation.unknowAggregateFunction": "集計関数またはグループが想定されていますが、[{type}]型の[{value}]が渡されました",
"kbn-esql-validation-autocomplete.esql.validation.unknownColumn": "不明な列[{name}]",

View file

@ -5170,10 +5170,7 @@
"kbn-esql-validation-autocomplete.esql.validation.expectedConstantValue": "[{fn}] 的参数必须为常数,收到的是 [{given}]",
"kbn-esql-validation-autocomplete.esql.validation.metadataBracketsDeprecation": "需要从 FROM METADATA 声明中移除方括号“[]”",
"kbn-esql-validation-autocomplete.esql.validation.missingFunction": "未知函数 [{name}]",
"kbn-esql-validation-autocomplete.esql.validation.noCombinationOfAggAndNonAggValues": "无法在 [STATS] 中组合聚合与非聚合值,找到了 [{expression}]",
"kbn-esql-validation-autocomplete.esql.validation.noNestedArgumentSupport": "聚合函数的参数必须为属性、文本或非聚合函数;找到了 [{argType}] 类型的 [{name}]",
"kbn-esql-validation-autocomplete.esql.validation.statsNoAggFunction": "[STATS] 中至少需要一个聚合函数,找到了 [{expression}]",
"kbn-esql-validation-autocomplete.esql.validation.statsNoArguments": "[STATS] 中至少需要一个聚合或分组表达式",
"kbn-esql-validation-autocomplete.esql.validation.typeOverwrite": "类型为 {fieldType} 的列 [{field}] 已重写为新类型:{newType}",
"kbn-esql-validation-autocomplete.esql.validation.unknowAggregateFunction": "应为聚合函数或组,但收到的是 [{type}] 类型的 [{value}]",
"kbn-esql-validation-autocomplete.esql.validation.unknownColumn": "未知列 [{name}]",