add regex support for constant conditionals

Fixes #11017
This commit is contained in:
Dan Hermann 2019-08-05 10:38:13 -05:00
parent 3293c22d2d
commit 120b6a7d04
2 changed files with 69 additions and 2 deletions

View file

@ -19,6 +19,7 @@ import org.logstash.config.ir.expression.BinaryBooleanExpression;
import org.logstash.config.ir.expression.BooleanExpression;
import org.logstash.config.ir.expression.EventValueExpression;
import org.logstash.config.ir.expression.Expression;
import org.logstash.config.ir.expression.RegexValueExpression;
import org.logstash.config.ir.expression.ValueExpression;
import org.logstash.config.ir.expression.binary.And;
import org.logstash.config.ir.expression.binary.Eq;
@ -184,6 +185,11 @@ public interface EventCondition {
expression.getRight() instanceof EventValueExpression;
}
private static boolean vAndR(final BinaryBooleanExpression expression) {
return expression.getLeft() instanceof ValueExpression &&
expression.getRight() instanceof RegexValueExpression;
}
private static EventCondition truthy(final Truthy truthy) {
final EventCondition condition;
final Expression inner = truthy.getExpression();
@ -201,8 +207,13 @@ public interface EventCondition {
final Expression uright = regex.getRight();
if (eAndV(regex)) {
condition = new EventCondition.Compiler.FieldMatches(
((EventValueExpression) uleft).getFieldName(),
((ValueExpression) uright).get().toString()
((EventValueExpression) uleft).getFieldName(),
((ValueExpression) uright).get().toString()
);
} else if (vAndR(regex)) {
condition = new EventCondition.Compiler.ConstantMatches(
((ValueExpression) uleft).get(),
((RegexValueExpression) uright).get().toString()
);
} else {
throw new EventCondition.Compiler.UnexpectedTypeException(uleft, uright);
@ -463,6 +474,24 @@ public interface EventCondition {
}
}
private static final class ConstantMatches implements EventCondition {
private final boolean matches;
private ConstantMatches(final Object constant, final String regex) {
this.matches = constant instanceof String &&
!(RubyUtil.RUBY.newString((String) constant).match(
WorkerLoop.THREAD_CONTEXT.get(),
RubyUtil.RUBY.newString(regex)).isNil());
}
@Override
public boolean fulfilled(final JrubyEventExtLibrary.RubyEvent event) {
return matches;
}
}
private static final class ConstantStringInField implements EventCondition {
private final FieldReference field;

View file

@ -249,6 +249,43 @@ public final class CompiledPipelineTest extends RubyEnvTestCase {
assertCorrectFieldToFieldComparison(gte, 7, 8, false);
}
@Test
public void correctlyCompilesRegexMatchesWithConstant() throws IncompleteSourceWithMetadataException {
verifyRegex("=~", 1);
}
@Test
public void correctlyCompilesRegexNoMatchesWithConstant() throws IncompleteSourceWithMetadataException {
verifyRegex("!~", 0);
}
private void verifyRegex(String operator, int expectedEvents)
throws IncompleteSourceWithMetadataException {
final Event event = new Event();
final JrubyEventExtLibrary.RubyEvent testEvent =
JrubyEventExtLibrary.RubyEvent.newRubyEvent(RubyUtil.RUBY, event);
new CompiledPipeline(
ConfigCompiler.configToPipelineIR(
"input {mockinput{}} output { " +
String.format("if \"z\" %s /z/ { ", operator) +
" mockoutput{} } }",
false
),
new CompiledPipelineTest.MockPluginFactory(
Collections.singletonMap("mockinput", () -> null),
Collections.singletonMap("mockaddfilter", () -> null),
Collections.singletonMap("mockoutput", mockOutputSupplier())
)
).buildExecution()
.compute(RubyUtil.RUBY.newArray(testEvent), false, false);
final Collection<JrubyEventExtLibrary.RubyEvent> outputEvents = EVENT_SINKS.get(runId);
MatcherAssert.assertThat(outputEvents.size(), CoreMatchers.is(expectedEvents));
MatcherAssert.assertThat(outputEvents.contains(testEvent), CoreMatchers.is(expectedEvents >= 1));
outputEvents.clear();
}
@Test
public void equalityCheckOnCompositeField() throws Exception {
final PipelineIR pipelineIR = ConfigCompiler.configToPipelineIR(
@ -386,6 +423,7 @@ public final class CompiledPipelineTest extends RubyEnvTestCase {
final Event event) throws IncompleteSourceWithMetadataException {
final JrubyEventExtLibrary.RubyEvent testEvent =
JrubyEventExtLibrary.RubyEvent.newRubyEvent(RubyUtil.RUBY, event);
new CompiledPipeline(
ConfigCompiler.configToPipelineIR(
"input {mockinput{}} filter { " +