diff --git a/server/genquery2/dsl/lexer.l b/server/genquery2/dsl/lexer.l index c965d5042e..f189907c8a 100644 --- a/server/genquery2/dsl/lexer.l +++ b/server/genquery2/dsl/lexer.l @@ -101,7 +101,7 @@ ")" return yy::parser::make_PAREN_CLOSE(loc); [a-zA-Z][a-zA-Z0-9_]* return yy::parser::make_IDENTIFIER(YYText(), loc); [0-9]+ return yy::parser::make_POSITIVE_INTEGER(YYText(), loc); --?[0-9]+ return yy::parser::make_INTEGER(YYText(), loc); +-[0-9]+ return yy::parser::make_NEGATIVE_INTEGER(YYText(), loc); . throw yy::parser::syntax_error{loc, fmt::format("invalid character: {}", YYText())}; <> return yy::parser::make_END_OF_INPUT(loc); diff --git a/server/genquery2/dsl/parser.y b/server/genquery2/dsl/parser.y index 53f81bbce8..793d49c638 100644 --- a/server/genquery2/dsl/parser.y +++ b/server/genquery2/dsl/parser.y @@ -43,6 +43,7 @@ This option causes make_* functions to be generated for each token kind. #include "irods/private/genquery2_ast_types.hpp" #include + #include #include #include @@ -109,7 +110,7 @@ This option causes make_* functions to be generated for each token kind. IDENTIFIER STRING_LITERAL POSITIVE_INTEGER - INTEGER + NEGATIVE_INTEGER ; %token END_OF_INPUT 0 @@ -138,11 +139,13 @@ rules only. %type range; %type selection; %type column; +%type >> arg_list; %type function; %type condition; %type condition_expression; %type > list_of_string_literals; %type > list_of_identifiers; +%type integer; %start genquery /* Defines where grammar starts */ @@ -198,9 +201,20 @@ column: | CAST PAREN_OPEN IDENTIFIER AS IDENTIFIER PAREN_CLOSE { $$ = gq2_detail::column{$3, $5}; } | CAST PAREN_OPEN IDENTIFIER AS IDENTIFIER PAREN_OPEN POSITIVE_INTEGER PAREN_CLOSE PAREN_CLOSE { $$ = gq2_detail::column{$3, fmt::format("{}({})", $5, $7)}; } +arg_list: + %empty { /* Generate an empty argument list. */ } + | STRING_LITERAL { $$.emplace_back($1); } + | integer { $$.emplace_back($1); } + | column { $$.emplace_back($1); } + | function { $$.emplace_back($1); } + | arg_list COMMA STRING_LITERAL + | arg_list COMMA integer { $1.emplace_back($3); std::swap($$, $1); } + | arg_list COMMA column { $1.emplace_back($3); std::swap($$, $1); } + | arg_list COMMA function { $1.emplace_back($3); std::swap($$, $1); } + ; + function: - IDENTIFIER PAREN_OPEN column PAREN_CLOSE { $$ = gq2_detail::function{std::move($1), gq2_detail::column{std::move($3)}}; } - /*IDENTIFIER PAREN_OPEN IDENTIFIER PAREN_CLOSE { $$ = gq2_detail::function{std::move($1), gq2_detail::column{std::move($3)}}; }*/ + IDENTIFIER PAREN_OPEN arg_list PAREN_CLOSE { $$ = gq2_detail::function{std::move($1), std::move($3)}; } conditions: condition { $$ = gq2_detail::conditions{std::move($1)}; } @@ -237,6 +251,10 @@ list_of_identifiers: IDENTIFIER { $$ = std::vector{std::move($1)}; } | list_of_identifiers COMMA IDENTIFIER { $1.push_back(std::move($3)); std::swap($$, $1); } +integer: + POSITIVE_INTEGER + | NEGATIVE_INTEGER { $$ = std::move($1); } + %% auto yy::parser::error(const yy::location& _loc, const std::string& _msg) -> void diff --git a/server/genquery2/include/irods/private/genquery2_ast_types.hpp b/server/genquery2/include/irods/private/genquery2_ast_types.hpp index 019e64212f..f25144bbc7 100644 --- a/server/genquery2/include/irods/private/genquery2_ast_types.hpp +++ b/server/genquery2/include/irods/private/genquery2_ast_types.hpp @@ -33,14 +33,14 @@ namespace irods::experimental::genquery2 { function() = default; - function(std::string name, column column) + function(std::string name, std::vector> _arguments) : name{std::move(name)} - , column{std::move(column)} + , arguments{std::move(_arguments)} { } std::string name; - column column; + std::vector> arguments; }; // struct function struct condition_like diff --git a/server/genquery2/src/genquery2_sql.cpp b/server/genquery2/src/genquery2_sql.cpp index e8c9572081..770cd1ceda 100644 --- a/server/genquery2/src/genquery2_sql.cpp +++ b/server/genquery2/src/genquery2_sql.cpp @@ -865,30 +865,54 @@ namespace irods::experimental::genquery2 auto to_sql(gq_state& _state, const function& _function) -> std::string { - const auto iter = column_name_mappings.find(_function.column.name); + std::vector args; + args.reserve(_function.arguments.size()); + + std::for_each(std::begin(_function.arguments), std::end(_function.arguments), [&](auto&& _arg) { + if (const auto* p = std::get_if(&_arg); p) { +#if 1 + args.emplace_back(fmt::format("'{}'", *p)); +#else + args.emplace_back("?"); + _state.values.emplace_back(*p); +#endif + return; + } - if (iter == std::end(column_name_mappings)) { - throw std::invalid_argument{fmt::format("unknown column: {}", _function.column.name)}; - } + if (const auto* p = std::get_if(&_arg); p) { + args.emplace_back(to_sql(_state, *p)); + return; + } - // Capture all column objects as some parts of the implementation need to access them later in - // order to generate the proper SQL. - _state.ast_column_ptrs.push_back(&_function.column); + if (const auto* p = std::get_if(&_arg); p) { + const auto iter = column_name_mappings.find(p->name); - auto [is_special_column, table_alias] = - setup_column_for_post_processing(_state, _function.column, iter->second); - const std::string_view alias = - is_special_column ? table_alias : _state.table_aliases.at(std::string{iter->second.table}); + if (iter == std::end(column_name_mappings)) { + throw std::invalid_argument{fmt::format("unknown column: {}", p->name)}; + } - if (_function.column.type_name.empty()) { - return fmt::format("{}({}.{})", _function.name, alias, iter->second.name); - } + // Capture all column objects as some parts of the implementation need to access them later in + // order to generate the proper SQL. + _state.ast_column_ptrs.push_back(&*p); + + auto [is_special_column, table_alias] = + setup_column_for_post_processing(_state, *p, iter->second); + const std::string_view alias = + is_special_column ? table_alias : _state.table_aliases.at(std::string{iter->second.table}); + + if (p->type_name.empty()) { + args.emplace_back(fmt::format("{}.{}", alias, iter->second.name)); + return; + } + + args.emplace_back(fmt::format("cast({}.{} as {})", + alias, + iter->second.name, + p->type_name)); + } + }); - return fmt::format("{}(cast({}.{} as {}))", - _function.name, - alias, - iter->second.name, - _function.column.type_name); + return fmt::format("{}({})", _function.name, fmt::join(args, ", ")); } // to_sql auto to_sql(gq_state& _state, const selections& _selections) -> std::string