[ES|QL] Fixes the wrong source validation in case of unknown patterns (#218352)

## Summary

Closes https://github.com/elastic/kibana/issues/191556
This commit is contained in:
Drew Tate 2025-04-16 09:00:53 -06:00 committed by GitHub
parent a2b70a0d45
commit eec1a9156a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 132 additions and 22 deletions

View file

@ -58,6 +58,12 @@ export const validationFromCommandTestSuite = (setup: helpers.Setup) => {
await expectErrors('fRoM *,-.*', []);
await expectErrors('fRoM .secret_index', []);
await expectErrors('from my-index', []);
await expectErrors('FROM index, missingIndex*', []);
await expectErrors('FROM index, lol*catz', []);
await expectErrors('FROM index*, lol*catz', []);
await expectErrors('FROM missingIndex*, index', []);
await expectErrors('FROM missingIndex*, missingIndex2*, index', []);
});
test('errors on trailing comma', async () => {
@ -95,6 +101,17 @@ export const validationFromCommandTestSuite = (setup: helpers.Setup) => {
await expectErrors(`FROM indexes*`, ['Unknown index [indexes*]']);
await expectErrors('from numberField', ['Unknown index [numberField]']);
await expectErrors('FROM policy', ['Unknown index [policy]']);
await expectErrors('FROM index, missingIndex', ['Unknown index [missingIndex]']);
await expectErrors('FROM missingIndex, index', ['Unknown index [missingIndex]']);
await expectErrors('FROM *missingIndex, missingIndex2, index', [
'Unknown index [missingIndex2]',
]);
await expectErrors('FROM missingIndex*', ['Unknown index [missingIndex*]']);
await expectErrors('FROM *missingIndex, missing*Index2', [
'Unknown index [*missingIndex]',
'Unknown index [missing*Index2]',
]);
});
});

View file

@ -8,7 +8,7 @@
*/
import { ESQLAstTimeseriesCommand, ESQLMessage } from '@kbn/esql-ast';
import { ESQLFunction, ESQLSource } from '@kbn/esql-ast/src/types';
import { ESQLFunction } from '@kbn/esql-ast/src/types';
import {
isAggFunction,
isFunctionOperatorParam,
@ -16,7 +16,7 @@ import {
} from '../../../shared/helpers';
import { ReferenceMaps } from '../../types';
import { isFunctionItem, isLiteralItem } from '../../../..';
import { validateSource } from '../../validation';
import { validateSources } from '../../validation';
/**
* Validates the TIMESERIES source command:
@ -58,16 +58,6 @@ const findNestedAggFunctionInAggFunction = (agg: ESQLFunction): ESQLFunction | u
}
};
function validateSources(sources: ESQLSource[], references: ReferenceMaps): ESQLMessage[] {
const messages: ESQLMessage[] = [];
for (const source of sources) {
messages.push(...validateSource(source, references));
}
return messages;
}
/**
* Looks for first nested aggregate function in another aggregate a function,
* recursively.

View file

@ -9662,6 +9662,31 @@
"error": [],
"warning": []
},
{
"query": "FROM index, missingIndex*",
"error": [],
"warning": []
},
{
"query": "FROM index, lol*catz",
"error": [],
"warning": []
},
{
"query": "FROM index*, lol*catz",
"error": [],
"warning": []
},
{
"query": "FROM missingIndex*, index",
"error": [],
"warning": []
},
{
"query": "FROM missingIndex*, missingIndex2*, index",
"error": [],
"warning": []
},
{
"query": "from index,",
"error": [
@ -9741,6 +9766,42 @@
],
"warning": []
},
{
"query": "FROM index, missingIndex",
"error": [
"Unknown index [missingIndex]"
],
"warning": []
},
{
"query": "FROM missingIndex, index",
"error": [
"Unknown index [missingIndex]"
],
"warning": []
},
{
"query": "FROM *missingIndex, missingIndex2, index",
"error": [
"Unknown index [missingIndex2]"
],
"warning": []
},
{
"query": "FROM missingIndex*",
"error": [
"Unknown index [missingIndex*]"
],
"warning": []
},
{
"query": "FROM *missingIndex, missing*Index2",
"error": [
"Unknown index [*missingIndex]",
"Unknown index [missing*Index2]"
],
"warning": []
},
{
"query": "from index metadata _id",
"error": [],

View file

@ -24,6 +24,7 @@ import {
areFieldAndVariableTypesCompatible,
getColumnExists,
getCommandDefinition,
hasWildcard,
isColumnItem,
isFunctionItem,
isOptionItem,
@ -282,11 +283,12 @@ function validateCommand(
locations: arg.location,
})
);
} else if (isSourceItem(arg)) {
messages.push(...validateSource(arg, references));
}
}
}
const sources = command.args.filter((arg) => isSourceItem(arg)) as ESQLSource[];
messages.push(...validateSources(sources, references));
}
}
@ -386,16 +388,56 @@ function validateUnsupportedTypeFields(fields: Map<string, ESQLRealField>, ast:
return messages;
}
export function validateSource(source: ESQLSource, { sources }: ReferenceMaps) {
export function validateSources(
sources: ESQLSource[],
{ sources: availableSources }: ReferenceMaps
) {
const messages: ESQLMessage[] = [];
if (source.incomplete) {
return messages;
const knownIndexNames = [];
const knownIndexPatterns = [];
const unknownIndexNames = [];
const unknownIndexPatterns = [];
for (const source of sources) {
if (source.incomplete) {
return messages;
}
if (source.sourceType === 'index') {
const index = source.index;
const sourceName = source.cluster ? source.name : index?.valueUnquoted;
if (!sourceName) continue;
if (sourceExists(sourceName, availableSources) && !hasWildcard(sourceName)) {
knownIndexNames.push(source);
}
if (sourceExists(sourceName, availableSources) && hasWildcard(sourceName)) {
knownIndexPatterns.push(source);
}
if (!sourceExists(sourceName, availableSources) && !hasWildcard(sourceName)) {
unknownIndexNames.push(source);
}
if (!sourceExists(sourceName, availableSources) && hasWildcard(sourceName)) {
unknownIndexPatterns.push(source);
}
}
}
if (source.sourceType === 'index') {
const index = source.index;
const indexName = source.cluster ? source.name : index?.valueUnquoted;
if (indexName && !sourceExists(indexName, sources)) {
unknownIndexNames.forEach((source) => {
messages.push(
getMessageFromId({
messageId: 'unknownIndex',
values: { name: source.name },
locations: source.location,
})
);
});
if (knownIndexNames.length + unknownIndexNames.length + knownIndexPatterns.length === 0) {
// only if there are no known index names, no known index patterns, and no unknown
// index names do we worry about creating errors for unknown index patterns
unknownIndexPatterns.forEach((source) => {
messages.push(
getMessageFromId({
messageId: 'unknownIndex',
@ -403,7 +445,7 @@ export function validateSource(source: ESQLSource, { sources }: ReferenceMaps) {
locations: source.location,
})
);
}
});
}
return messages;