Skip to content

Commit

Permalink
[?] Add support for multi-arg, nested functions to GenQuery2.
Browse files Browse the repository at this point in the history
  • Loading branch information
korydraughn committed Jun 7, 2024
1 parent 411b1c8 commit 222f4d7
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 26 deletions.
2 changes: 1 addition & 1 deletion server/genquery2/dsl/lexer.l
Original file line number Diff line number Diff line change
Expand Up @@ -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())};
<<EOF>> return yy::parser::make_END_OF_INPUT(loc);

Expand Down
24 changes: 21 additions & 3 deletions server/genquery2/dsl/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ This option causes make_* functions to be generated for each token kind.
#include "irods/private/genquery2_ast_types.hpp"

#include <string>
#include <variant>
#include <vector>

#include <fmt/format.h>
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -138,11 +139,13 @@ rules only.
%type <gq2_detail::range> range;
%type <gq2_detail::selection> selection;
%type <gq2_detail::column> column;
%type <std::vector<std::variant<std::string, gq2_detail::column, gq2_detail::function>>> arg_list;
%type <gq2_detail::function> function;
%type <gq2_detail::condition> condition;
%type <gq2_detail::condition_expression> condition_expression;
%type <std::vector<std::string>> list_of_string_literals;
%type <std::vector<std::string>> list_of_identifiers;
%type <std::string> integer;

%start genquery /* Defines where grammar starts */

Expand Down Expand Up @@ -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)}; }
Expand Down Expand Up @@ -237,6 +251,10 @@ list_of_identifiers:
IDENTIFIER { $$ = std::vector<std::string>{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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ namespace irods::experimental::genquery2
{
function() = default;

function(std::string name, column column)
function(std::string name, std::vector<std::variant<std::string, column, function>> _arguments)
: name{std::move(name)}
, column{std::move(column)}
, arguments{std::move(_arguments)}
{
}

std::string name;
column column;
std::vector<std::variant<std::string, column, function>> arguments;
}; // struct function

struct condition_like
Expand Down
62 changes: 43 additions & 19 deletions server/genquery2/src/genquery2_sql.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string> 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<std::string>(&_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<function>(&_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<column>(&_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
Expand Down

0 comments on commit 222f4d7

Please sign in to comment.