Skip to content

Commit

Permalink
Merge pull request #164 from bshifter/filterx-null-coalesce
Browse files Browse the repository at this point in the history
Filterx null coalesce operator
  • Loading branch information
alltilla authored Jun 19, 2024
2 parents 0b0f87e + 1846576 commit ffea1bf
Show file tree
Hide file tree
Showing 16 changed files with 449 additions and 9 deletions.
1 change: 1 addition & 0 deletions lib/cfg-grammar.y
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ main_location_print (FILE *yyo, YYLTYPE const * const yylocp)
/* operators in the filter language, the order of this determines precedence */
%right KW_ASSIGN 9000
%right '?' ':'
%right KW_NULL_COALESCING
%left KW_OR 9010
%left KW_AND 9020
%left KW_STR_EQ 9030 KW_STR_NE 9031, KW_TA_EQ 9032, KW_TA_NE 9033, KW_TAV_EQ 9034, KW_TAV_NE 9035
Expand Down
1 change: 1 addition & 0 deletions lib/cfg-lex.l
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ filterx_word [^ \#'"/\(\)\{\}\[\]\\;\r\n\t,|\.@:]
<INITIAL,filterx>!== { return KW_TAV_NE; }
<filterx>\+= { return KW_PLUS_ASSIGN; }
<filterx>=~ { return KW_REGEXP_MATCH; }
<filterx>\?\? { return KW_NULL_COALESCING; }
<INITIAL,filterx>(-|\+)?{digit}+\.{digit}+ { yylval->fnum = strtod(yytext, NULL); return LL_FLOAT; }
<INITIAL,filterx>0x{xdigit}+ {
Expand Down
2 changes: 2 additions & 0 deletions lib/filterx/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ set(FILTERX_HEADERS
filterx/func-vars.h
filterx/func-unset-empties.h
filterx/expr-plus.h
filterx/expr-null-coalesce.h
PARENT_SCOPE
)

Expand Down Expand Up @@ -88,6 +89,7 @@ set(FILTERX_SOURCES
filterx/func-unset-empties.c
filterx/expr-plus.c
filterx/filterx-private.c
filterx/expr-null-coalesce.c
PARENT_SCOPE
)

Expand Down
4 changes: 3 additions & 1 deletion lib/filterx/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ filterxinclude_HEADERS = \
lib/filterx/func-len.h \
lib/filterx/func-vars.h \
lib/filterx/func-unset-empties.h \
lib/filterx/filterx-private.h
lib/filterx/filterx-private.h \
lib/filterx/expr-null-coalesce.h


filterx_sources = \
Expand Down Expand Up @@ -89,6 +90,7 @@ filterx_sources = \
lib/filterx/func-vars.c \
lib/filterx/func-unset-empties.c \
lib/filterx/filterx-private.c \
lib/filterx/expr-null-coalesce.c \
lib/filterx/filterx-grammar.y

BUILT_SOURCES += \
Expand Down
67 changes: 67 additions & 0 deletions lib/filterx/expr-null-coalesce.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2024 Axoflow
* Copyright (c) 2024 shifter <shifter@axoflow.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As an additional exemption you are allowed to compile & link against the
* OpenSSL libraries as published by the OpenSSL project. See the file
* COPYING for details.
*
*/

#include "filterx/expr-null-coalesce.h"
#include "filterx/object-null.h"
#include "filterx/filterx-eval.h"
#include "filterx/object-message-value.h"

typedef struct _FilterXNullCoalesce FilterXNullCoalesce;

struct _FilterXNullCoalesce
{
FilterXBinaryOp super;
};

static FilterXObject *
_eval(FilterXExpr *s)
{
FilterXNullCoalesce *self = (FilterXNullCoalesce *) s;

FilterXObject *lhs_object = filterx_expr_eval(self->super.lhs);
if (!lhs_object || filterx_object_is_type(lhs_object, &FILTERX_TYPE_NAME(null))
|| (filterx_object_is_type(lhs_object, &FILTERX_TYPE_NAME(message_value))
&& filterx_message_value_get_type(lhs_object) == LM_VT_NULL))
{
if (!lhs_object)
{
msg_debug("FILTERX null coalesce supressing error:",
filterx_format_last_error());
filterx_eval_clear_errors();
}
FilterXObject *rhs_object = filterx_expr_eval(self->super.rhs);
filterx_object_unref(lhs_object);
return rhs_object;
}
return lhs_object;
}

FilterXExpr *
filterx_null_coalesce_new(FilterXExpr *lhs, FilterXExpr *rhs)
{
FilterXNullCoalesce *self = g_new0(FilterXNullCoalesce, 1);
filterx_binary_op_init_instance(&self->super, lhs, rhs);
self->super.super.eval = _eval;
return &self->super.super;
}
32 changes: 32 additions & 0 deletions lib/filterx/expr-null-coalesce.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2024 Axoflow
* Copyright (c) 2024 shifter <shifter@axoflow.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As an additional exemption you are allowed to compile & link against the
* OpenSSL libraries as published by the OpenSSL project. See the file
* COPYING for details.
*
*/

#ifndef FILTERX_NULL_COALESCE_H_INCLUDED
#define FILTERX_NULL_COALESCE_H_INCLUDED

#include "filterx/filterx-expr.h"

FilterXExpr *filterx_null_coalesce_new(FilterXExpr *lhs, FilterXExpr *rhs);

#endif
1 change: 1 addition & 0 deletions lib/filterx/filterx-eval.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ void filterx_eval_set_context(FilterXEvalContext *context);
gboolean filterx_eval_exec_statements(FilterXEvalContext *context, GList *statements, LogMessage *msg);
void filterx_eval_sync_scope_and_message(FilterXScope *scope, LogMessage *msg);
const gchar *filterx_eval_get_last_error(void);
EVTTAG *filterx_format_last_error(void);
void filterx_eval_clear_errors(void);

void filterx_eval_store_weak_ref(FilterXObject *object);
Expand Down
2 changes: 2 additions & 0 deletions lib/filterx/filterx-expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ filterx_binary_op_init_instance(FilterXBinaryOp *self, FilterXExpr *lhs, FilterX
{
filterx_expr_init_instance(&self->super);
self->super.free_fn = filterx_binary_op_free_method;
g_assert(lhs);
g_assert(rhs);
self->lhs = lhs;
self->rhs = rhs;
}
Expand Down
10 changes: 10 additions & 0 deletions lib/filterx/filterx-grammar.ym
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
#include "filterx/expr-shorthand.h"
#include "filterx/expr-regexp.h"
#include "filterx/expr-plus.h"
#include "filterx/expr-null-coalesce.h"

#include "template/templates.h"

Expand Down Expand Up @@ -141,6 +142,7 @@ construct_template_expr(LogTemplate *template)
%type <ptr> if
%type <ptr> codeblock
%type <ptr> ternary
%type <ptr> default

%%

Expand Down Expand Up @@ -316,6 +318,7 @@ expr
| expr '+' expr { $$ = filterx_operator_plus_new($1, $3); }
| '(' expr ')' { $$ = $2; }
| ternary { $$ = $1; }
| default { $$ = $1; }
| KW_ISSET '(' expr ')' { $$ = filterx_isset_new($3); }
| KW_UNSET '(' expr ')' { $$ = filterx_unset_new($3); }
| regexp_match { $$ = $1; }
Expand Down Expand Up @@ -493,6 +496,13 @@ ternary
}
;

default
: expr KW_NULL_COALESCING expr
{
$$ = filterx_null_coalesce_new($1, $3);
}
;

/* INCLUDE_RULES */

%%
2 changes: 1 addition & 1 deletion lib/filterx/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ add_unit_test(LIBTEST CRITERION TARGET test_func_istype DEPENDS json-plugin ${JS
add_unit_test(LIBTEST CRITERION TARGET test_func_unset_empties DEPENDS json-plugin ${JSONC_LIBRARY})
add_unit_test(LIBTEST CRITERION TARGET test_expr_function DEPENDS json-plugin ${JSONC_LIBRARY})
add_unit_test(LIBTEST CRITERION TARGET test_expr_regexp DEPENDS json-plugin ${JSONC_LIBRARY})

add_unit_test(LIBTEST CRITERION TARGET test_expr_null_coalesce DEPENDS json-plugin ${JSONC_LIBRARY})
6 changes: 5 additions & 1 deletion lib/filterx/tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ lib_filterx_tests_TESTS = \
lib/filterx/tests/test_type_registry \
lib/filterx/tests/test_func_istype \
lib/filterx/tests/test_func_unset_empties \
lib/filterx/tests/test_expr_regexp
lib/filterx/tests/test_expr_regexp \
lib/filterx/tests/test_expr_null_coalesce

EXTRA_DIST += lib/filterx/tests/CMakeLists.txt

Expand Down Expand Up @@ -84,3 +85,6 @@ lib_filterx_tests_test_expr_function_LDADD = $(TEST_LDADD) $(JSON_LIBS)

lib_filterx_tests_test_expr_regexp_CFLAGS = $(TEST_CFLAGS)
lib_filterx_tests_test_expr_regexp_LDADD = $(TEST_LDADD) $(JSON_LIBS)

lib_filterx_tests_test_expr_null_coalesce_CFLAGS = $(TEST_CFLAGS)
lib_filterx_tests_test_expr_null_coalesce_LDADD = $(TEST_LDADD) $(JSON_LIBS)
143 changes: 143 additions & 0 deletions lib/filterx/tests/test_expr_null_coalesce.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
* Copyright (c) 2024 Axoflow
* Copyright (c) 2024 shifter <shifter@axoflow.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As an additional exemption you are allowed to compile & link against the
* OpenSSL libraries as published by the OpenSSL project. See the file
* COPYING for details.
*
*/

#include <criterion/criterion.h>
#include "libtest/cr_template.h"
#include "libtest/filterx-lib.h"

#include "filterx/filterx-object.h"
#include "filterx/object-primitive.h"
#include "filterx/expr-comparison.h"
#include "filterx/filterx-expr.h"
#include "filterx/expr-literal.h"
#include "filterx/object-string.h"
#include "filterx/object-null.h"
#include "filterx/object-datetime.h"
#include "filterx/object-message-value.h"
#include "filterx/expr-null-coalesce.h"
#include "filterx/func-istype.h"
#include "filterx/filterx-eval.h"
#include "filterx/func-len.h"
#include "filterx/expr-function.h"

#include "apphook.h"
#include "scratch-buffers.h"

Test(expr_null_coalesce, test_coalescing_soft_null)
{
FilterXExpr *coalesce = filterx_null_coalesce_new(filterx_literal_new(filterx_null_new()),
filterx_non_literal_new(filterx_test_unknown_object_new()));
cr_assert(coalesce);
FilterXObject *res = filterx_expr_eval(coalesce);
cr_assert(res);
cr_assert(filterx_object_is_type(res, &FILTERX_TYPE_NAME(test_unknown_object)));
filterx_expr_unref(coalesce);
filterx_object_unref(res);
}

Test(expr_null_coalesce, test_coalescing_supressing_lhs_eval_error)
{
FilterXExpr *err_expr = filterx_dummy_error_new("lhs error");
cr_assert_not_null(err_expr);

// passing errorous expression as lhs
FilterXExpr *coalesce = filterx_null_coalesce_new(err_expr, filterx_non_literal_new(filterx_test_unknown_object_new()));
cr_assert(coalesce);

// eval returns rhs value, since lhs fails during eval
FilterXObject *res = filterx_expr_eval(coalesce);
cr_assert(res);
cr_assert(filterx_object_is_type(res, &FILTERX_TYPE_NAME(test_unknown_object)));

// lhs expr eval errors must supressed by null_coalesce
const gchar *last_error = filterx_eval_get_last_error();
cr_assert_null(last_error);

filterx_expr_unref(coalesce);
filterx_object_unref(res);
}

Test(expr_null_coalesce, test_coalescing_keep_rhs_eval_error)
{
const gchar *error_msg = "rhs error";
FilterXExpr *err_expr = filterx_dummy_error_new(error_msg);

// passing errorous expression as rhs
FilterXExpr *coalesce = filterx_null_coalesce_new(filterx_non_literal_new(filterx_null_new()),
err_expr);
cr_assert(coalesce);

// null_coalesce returns null, since lhs is null and rhs fails
FilterXObject *res = filterx_expr_eval(coalesce);
cr_assert_null(res);

// rhs expr eval errors must remain intact
const gchar *last_error = filterx_eval_get_last_error();
cr_assert_not_null(last_error);
cr_assert_str_eq(error_msg, last_error);

filterx_expr_unref(coalesce);
filterx_object_unref(res);
}

Test(expr_null_coalesce, test_coalescing_keep_rhs_eval_error_on_double_fail)
{
FilterXExpr *err_expr_lhs = filterx_dummy_error_new("lhs error");

const gchar *error_msg = "rhs error";
FilterXExpr *err_expr_rhs = filterx_dummy_error_new(error_msg);

// passing errorous expressions
FilterXExpr *coalesce = filterx_null_coalesce_new(err_expr_lhs, err_expr_rhs);
cr_assert(coalesce);

// null_coalesce returns null, since both lhs and rhs fails
FilterXObject *res = filterx_expr_eval(coalesce);
cr_assert_null(res);

// rhs expr eval errors must remain intact
const gchar *last_error = filterx_eval_get_last_error();
cr_assert_not_null(last_error);
cr_assert_str_eq(error_msg, last_error);

filterx_expr_unref(coalesce);
filterx_object_unref(res);
}

static void
setup(void)
{
app_startup();
init_libtest_filterx();
}

static void
teardown(void)
{
scratch_buffers_explicit_gc();
deinit_libtest_filterx();
app_shutdown();
}

TestSuite(expr_null_coalesce, .init = setup, .fini = teardown);
Loading

0 comments on commit ffea1bf

Please sign in to comment.