sources/gen_sql.c (3,303 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#if defined(CQL_AMALGAM_LEAN) && !defined(CQL_AMALGAM_GEN_SQL)
// stubs to avoid link errors,
cql_noexport void gen_init() {}
cql_export void gen_cleanup() {}
cql_noexport void gen_misc_attrs_to_stdout(ast_node *ast) {}
cql_noexport void gen_to_stdout(ast_node *ast, gen_func fn) {}
cql_noexport void gen_one_stmt_to_stdout(ast_node *ast) {}
cql_noexport void gen_stmt_list_to_stdout(ast_node *ast) {}
#else
// (re)generate equivalent SQL to what we parsed
// validate the tree shape in painful detail as we go
#include "cql.h"
#include "ast.h"
#include "gen_sql.h"
#include "sem.h"
#include "charbuf.h"
#include "symtab.h"
#include "encoders.h"
#include <string.h>
#include <stdlib.h>
// for dispatching expression types
typedef struct gen_expr_dispatch {
void (*func)(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new);
CSTR str;
int32_t pri_new;
} gen_expr_dispatch;
// for INDENT macros
#define output gen_output
static symtab *gen_stmts;
static symtab *gen_exprs;
static charbuf *gen_output;
static gen_sql_callbacks *gen_callbacks = NULL;
static symtab *used_alias_syms = NULL;
// forward references for things that appear out of order or mutually call each other
static void gen_select_core_list(ast_node *ast);
static void gen_groupby_list(ast_node *_Nonnull ast);
static void gen_orderby_list(ast_node *_Nonnull ast);
static void gen_stmt_list(ast_node *_Nullable ast);
static void gen_expr(ast_node *_Nonnull ast, int32_t pri);
static void gen_version_attrs(ast_node *_Nullable ast);
static void gen_col_def(ast_node *_Nonnull ast);
static void gen_query_parts(ast_node *ast);
static void gen_select_stmt(ast_node *_Nonnull ast);
static void gen_opt_where_without_new_line(ast_node *ast);
static void gen_opt_orderby(ast_node *ast);
static void gen_shape_arg(ast_node *ast);
static void gen_insert_list(ast_node *_Nullable ast);
static void gen_column_spec(ast_node *ast);
static void gen_from_shape(ast_node *ast);
static void gen_opt_filter_clause(ast_node *ast);
static void gen_if_not_exists(ast_node *ast, bool_t if_not_exist);
static void gen_shape_def(ast_node *ast);
static void gen_expr_names(ast_node *ast);
static void gen_opt_where(ast_node *ast);
static void gen_conflict_clause(ast_node *ast);
static void gen_call_stmt(ast_node *ast);
static void gen_shared_cte(ast_node *ast);
#define gen_printf(...) bprintf(output, __VA_ARGS__)
cql_noexport void gen_to_stdout(ast_node *ast, gen_func fn) {
gen_callbacks = NULL;
charbuf *gen_saved = output;
CHARBUF_OPEN(sql_out);
gen_set_output_buffer(&sql_out);
(*fn)(ast);
cql_output("%s", sql_out.ptr);
CHARBUF_CLOSE(sql_out);
output = gen_saved;
}
static bool_t suppress_attributes() {
return gen_callbacks && (gen_callbacks->mode == gen_mode_sql || gen_callbacks->mode == gen_mode_no_annotations);
}
static bool_t for_sqlite() {
return gen_callbacks && gen_callbacks->mode == gen_mode_sql;
}
cql_noexport void gen_stmt_list_to_stdout(ast_node *ast) {
gen_to_stdout(ast, gen_stmt_list);
}
cql_noexport void gen_one_stmt_to_stdout(ast_node *ast) {
gen_to_stdout(ast, gen_one_stmt);
cql_output(";\n");
}
cql_noexport void gen_misc_attrs_to_stdout(ast_node *ast) {
gen_to_stdout(ast, gen_misc_attrs);
}
cql_noexport void gen_with_callbacks(ast_node *ast, gen_func fn, gen_sql_callbacks *_callbacks) {
gen_callbacks = _callbacks;
(*fn)(ast);
gen_callbacks = NULL;
}
cql_noexport void gen_col_def_with_callbacks(ast_node *ast, gen_sql_callbacks *_callbacks) {
gen_with_callbacks(ast, gen_col_def, _callbacks);
}
cql_noexport void gen_statement_with_callbacks(ast_node *ast, gen_sql_callbacks *_callbacks) {
// works for statements or statement lists
if (is_ast_stmt_list(ast)) {
gen_stmt_level = -1; // the first statement list does not indent
gen_with_callbacks(ast, gen_stmt_list, _callbacks);
}
else {
gen_stmt_level = 0; // nested statement lists will indent
gen_with_callbacks(ast, gen_one_stmt, _callbacks);
}
}
cql_noexport void gen_set_output_buffer(struct charbuf *buffer) {
output = buffer;
}
static void gen_name(ast_node *ast) {
EXTRACT_STRING(name, ast);
gen_printf("%s", name);
}
static void gen_name_list(ast_node *list) {
Contract(is_ast_name_list(list));
for (ast_node *item = list; item; item = item->right) {
gen_name(item->left);
if (item->right) {
gen_printf(", ");
}
}
}
cql_noexport void gen_misc_attr_value_list(ast_node *ast) {
Contract(is_ast_misc_attr_value_list(ast));
for (ast_node *item = ast; item; item = item->right) {
gen_misc_attr_value(item->left);
if (item->right) {
gen_printf(", ");
}
}
}
cql_noexport void gen_misc_attr_value(ast_node *ast) {
if (is_ast_misc_attr_value_list(ast)) {
gen_printf("(");
gen_misc_attr_value_list(ast);
gen_printf(")");
}
else {
gen_root_expr(ast);
}
}
static void gen_misc_attr(ast_node *ast) {
Contract(is_ast_misc_attr(ast));
gen_printf("@ATTRIBUTE(");
if (is_ast_dot(ast->left)) {
gen_name(ast->left->left);
gen_printf(":");
gen_name(ast->left->right);
}
else {
gen_name(ast->left);
}
if (ast->right) {
gen_printf("=");
gen_misc_attr_value(ast->right);
}
gen_printf(")\n");
}
cql_noexport void gen_misc_attrs(ast_node *list) {
Contract(is_ast_misc_attrs(list));
// misc attributes don't go into the output if we are writing for Sqlite
if (suppress_attributes()) {
return;
}
for (ast_node *item = list; item; item = item->right) {
gen_misc_attr(item->left);
}
}
static void gen_data_type(ast_node *ast) {
if (is_ast_create_data_type(ast)) {
gen_printf("CREATE ");
gen_data_type(ast->left);
return;
}
else if (is_ast_notnull(ast)) {
gen_data_type(ast->left);
gen_printf(" NOT NULL");
return;
}
else if (is_ast_sensitive_attr(ast)) {
gen_data_type(ast->left);
if (!for_sqlite()) {
gen_printf(" @SENSITIVE");
}
return;
}
else if (is_ast_type_int(ast)) {
gen_printf("INTEGER");
} else if (is_ast_type_text(ast)) {
gen_printf("TEXT");
} else if (is_ast_type_blob(ast)) {
gen_printf("BLOB");
} else if (is_ast_type_object(ast)) {
gen_printf("OBJECT");
} else if (is_ast_type_long(ast)) {
gen_printf("LONG_INT");
} else if (is_ast_type_real(ast)) {
gen_printf("REAL");
} else if (is_ast_type_bool(ast)) {
gen_printf("BOOL");
} else {
Contract(is_ast_str(ast));
EXTRACT_STRING(name, ast);
gen_printf("%s", name);
return;
}
if (!for_sqlite()) {
if (ast->left) {
gen_printf("<");
gen_name(ast->left);
gen_printf(">");
}
}
}
static void gen_indexed_column(ast_node *ast) {
Contract(is_ast_indexed_column(ast));
EXTRACT_ANY_NOTNULL(expr, ast->left);
gen_root_expr(expr);
if (is_ast_asc(ast->right)) {
gen_printf(" ASC");
}
else if (is_ast_desc(ast->right)) {
gen_printf(" DESC");
}
}
static void gen_indexed_columns(ast_node *ast) {
Contract(is_ast_indexed_columns(ast));
for (ast_node *item = ast; item; item = item->right) {
gen_indexed_column(item->left);
if (item->right) {
gen_printf(", ");
}
}
}
static void gen_create_index_stmt(ast_node *ast) {
Contract(is_ast_create_index_stmt(ast));
EXTRACT_NOTNULL(create_index_on_list, ast->left);
EXTRACT_NOTNULL(flags_names_attrs, ast->right);
EXTRACT_NOTNULL(connector, flags_names_attrs->right);
EXTRACT_NOTNULL(index_names_and_attrs, connector->left);
EXTRACT_OPTION(flags, flags_names_attrs->left);
EXTRACT_NOTNULL(indexed_columns, index_names_and_attrs->left);
EXTRACT(opt_where, index_names_and_attrs->right);
EXTRACT_ANY(attrs, connector->right);
EXTRACT_STRING(index_name, create_index_on_list->left);
EXTRACT_STRING(table_name, create_index_on_list->right);
gen_printf("CREATE ");
if (flags & INDEX_UNIQUE) {
gen_printf("UNIQUE ");
}
gen_printf("INDEX ");
gen_if_not_exists(ast, !!(flags & INDEX_IFNE));
gen_printf("%s ON %s (", index_name, table_name);
gen_indexed_columns(indexed_columns);
gen_printf(")");
if (opt_where) {
gen_opt_where(opt_where);
}
gen_version_attrs(attrs);
}
static void gen_unq_def(ast_node *def) {
Contract(is_ast_unq_def(def));
EXTRACT_NOTNULL(indexed_columns_conflict_clause, def->right);
EXTRACT_NOTNULL(indexed_columns, indexed_columns_conflict_clause->left);
EXTRACT_ANY(conflict_clause, indexed_columns_conflict_clause->right);
if (def->left) {
EXTRACT_STRING(name, def->left);
gen_printf("CONSTRAINT %s ", name);
}
gen_printf("UNIQUE (");
gen_indexed_columns(indexed_columns);
gen_printf(")");
if (conflict_clause) {
gen_conflict_clause(conflict_clause);
}
}
static void gen_check_def(ast_node *def) {
Contract(is_ast_check_def(def));
if (def->left) {
EXTRACT_STRING(name, def->left);
gen_printf("CONSTRAINT %s ", name);
}
EXTRACT_ANY_NOTNULL(expr, def->right);
gen_printf("CHECK (");
gen_root_expr(expr);
gen_printf(")");
}
cql_noexport void gen_fk_action(int32_t action) {
switch (action) {
case FK_SET_NULL:
gen_printf("SET NULL");
break;
case FK_SET_DEFAULT:
gen_printf("SET DEFAULT");
break;
case FK_CASCADE:
gen_printf("CASCADE");
break;
case FK_RESTRICT:
gen_printf("RESTRICT");
break;
default:
// this is all that's left, it better be this...
Contract(action == FK_NO_ACTION);
gen_printf("NO ACTION");
break;
}
}
static void gen_fk_flags(int32_t flags) {
if (flags) {
gen_printf(" ");
}
int32_t action = (flags & FK_ON_UPDATE) >> 4;
if (action) {
gen_printf("ON UPDATE ");
gen_fk_action(action);
if (flags & (FK_ON_DELETE|FK_DEFERRABLES)) {
gen_printf(" ");
}
}
action = (flags & FK_ON_DELETE);
if (action) {
gen_printf("ON DELETE ");
gen_fk_action(action);
if (flags & FK_DEFERRABLES) {
gen_printf(" ");
}
}
if (flags & FK_DEFERRABLES) {
Contract(flags & (FK_DEFERRABLE|FK_NOT_DEFERRABLE));
if (flags & FK_DEFERRABLE) {
Contract(!(flags & FK_NOT_DEFERRABLE));
gen_printf("DEFERRABLE");
}
else {
gen_printf("NOT DEFERRABLE");
}
if (flags & FK_INITIALLY_IMMEDIATE) {
Contract(!(flags & FK_INITIALLY_DEFERRED));
gen_printf(" INITIALLY IMMEDIATE");
}
else if (flags & FK_INITIALLY_DEFERRED) {
gen_printf(" INITIALLY DEFERRED");
}
}
}
static void gen_fk_target_options(ast_node *ast) {
Contract(is_ast_fk_target_options(ast));
EXTRACT_NOTNULL(fk_target, ast->left);
EXTRACT_OPTION(flags, ast->right);
EXTRACT_STRING(table_name, fk_target->left);
EXTRACT_NAMED_NOTNULL(ref_list, name_list, fk_target->right);
gen_printf("REFERENCES ");
gen_printf("%s", table_name);
gen_printf(" (");
gen_name_list(ref_list);
gen_printf(")");
gen_fk_flags(flags);
}
static void gen_fk_def(ast_node *def) {
Contract(is_ast_fk_def(def));
EXTRACT(fk_info, def->right);
EXTRACT_NAMED_NOTNULL(src_list, name_list, fk_info->left);
EXTRACT_NOTNULL(fk_target_options, fk_info->right);
if (def->left) {
EXTRACT_STRING(name, def->left);
gen_printf("CONSTRAINT %s ", name);
}
gen_printf("FOREIGN KEY (");
gen_name_list(src_list);
gen_printf(") ");
gen_fk_target_options(fk_target_options);
}
static void gen_conflict_clause(ast_node *ast) {
Contract(is_ast_int(ast));
EXTRACT_OPTION(conflict_clause_opt, ast);
gen_printf(" ON CONFLICT ");
switch (conflict_clause_opt) {
case ON_CONFLICT_ROLLBACK:
gen_printf("ROLLBACK");
break;
case ON_CONFLICT_ABORT:
gen_printf("ABORT");
break;
case ON_CONFLICT_FAIL:
gen_printf("FAIL");
break;
case ON_CONFLICT_IGNORE:
gen_printf("IGNORE");
break;
case ON_CONFLICT_REPLACE:
gen_printf("REPLACE");
break;
}
}
static void gen_pk_def(ast_node *def) {
Contract(is_ast_pk_def(def));
EXTRACT_NOTNULL(indexed_columns_conflict_clause, def->right);
EXTRACT_NOTNULL(indexed_columns, indexed_columns_conflict_clause->left);
EXTRACT_ANY(conflict_clause, indexed_columns_conflict_clause->right);
if (def->left) {
EXTRACT_STRING(name, def->left);
gen_printf("CONSTRAINT %s ", name);
}
gen_printf("PRIMARY KEY (");
gen_indexed_columns(indexed_columns);
gen_printf(")");
if (conflict_clause) {
gen_conflict_clause(conflict_clause);
}
}
static void gen_version_and_proc(ast_node *ast)
{
Contract(is_ast_version_annotation(ast));
EXTRACT_OPTION(vers, ast->left);
gen_printf("%d", vers);
if (ast->right) {
if (is_ast_dot(ast->right)) {
EXTRACT_NOTNULL(dot, ast->right);
EXTRACT_STRING(lhs, dot->left);
EXTRACT_STRING(rhs, dot->right);
gen_printf(", %s:%s", lhs, rhs);
}
else
{
EXTRACT_STRING(name, ast->right);
gen_printf(", %s", name);
}
}
}
static void gen_recreate_attr(ast_node *attr) {
Contract (is_ast_recreate_attr(attr));
if (!suppress_attributes()) {
// attributes do not appear when writing out commands for Sqlite
gen_printf(" @RECREATE");
if (attr->left) {
EXTRACT_STRING(group_name, attr->left);
gen_printf("(%s)", group_name);
}
}
}
static void gen_create_attr(ast_node *attr) {
Contract (is_ast_create_attr(attr));
if (!suppress_attributes()) {
// attributes do not appear when writing out commands for Sqlite
gen_printf(" @CREATE(");
gen_version_and_proc(attr->left);
gen_printf(")");
}
}
static void gen_delete_attr(ast_node *attr) {
Contract (is_ast_delete_attr(attr));
// attributes do not appear when writing out commands for Sqlite
if (!suppress_attributes()) {
gen_printf(" @DELETE");
if (attr->left) {
gen_printf("(");
gen_version_and_proc(attr->left);
gen_printf(")");
}
}
}
static void gen_sensitive_attr(ast_node *attr) {
Contract (is_ast_sensitive_attr(attr));
if (!for_sqlite()) {
// attributes do not appear when writing out commands for Sqlite
gen_printf(" @SENSITIVE");
}
}
static void gen_col_attrs(ast_node *_Nullable attrs) {
for (ast_node *attr = attrs; attr; attr = attr->right) {
if (is_ast_create_attr(attr)) {
gen_create_attr(attr);
} else if (is_ast_sensitive_attr(attr)) {
gen_sensitive_attr(attr);
} else if (is_ast_delete_attr(attr)) {
gen_delete_attr(attr);
} else if (is_ast_col_attrs_not_null(attr)) {
gen_printf(" NOT NULL");
EXTRACT_ANY(conflict_clause, attr->left);
if (conflict_clause) {
gen_conflict_clause(conflict_clause);
}
} else if (is_ast_col_attrs_pk(attr)) {
EXTRACT_NOTNULL(autoinc_and_conflict_clause, attr->left);
EXTRACT(col_attrs_autoinc, autoinc_and_conflict_clause->left);
EXTRACT_ANY(conflict_clause, autoinc_and_conflict_clause->right);
gen_printf(" PRIMARY KEY");
if (conflict_clause) {
gen_conflict_clause(conflict_clause);
}
if (col_attrs_autoinc) {
gen_printf(" AUTOINCREMENT");
}
} else if (is_ast_col_attrs_unique(attr)) {
gen_printf(" UNIQUE");
if (attr->left) {
gen_conflict_clause(attr->left);
}
} else if (is_ast_col_attrs_hidden(attr)) {
gen_printf(" HIDDEN");
} else if (is_ast_col_attrs_fk(attr)) {
gen_printf(" ");
gen_fk_target_options(attr->left);
} else if (is_ast_col_attrs_check(attr)) {
gen_printf(" CHECK(");
gen_root_expr(attr->left);
gen_printf(") ");
} else if (is_ast_col_attrs_collate(attr)) {
gen_printf(" COLLATE ");
gen_root_expr(attr->left);
} else {
Contract(is_ast_col_attrs_default(attr));
gen_printf(" DEFAULT ");
gen_root_expr(attr->left);
}
}
}
static void gen_col_def(ast_node *def) {
Contract(is_ast_col_def(def));
EXTRACT_NOTNULL(col_def_type_attrs, def->left);
EXTRACT(misc_attrs, def->right);
EXTRACT_ANY(attrs, col_def_type_attrs->right);
EXTRACT_NOTNULL(col_def_name_type, col_def_type_attrs->left);
EXTRACT_STRING(name, col_def_name_type->left);
EXTRACT_ANY_NOTNULL(data_type, col_def_name_type->right);
if (misc_attrs) {
gen_misc_attrs(misc_attrs);
}
gen_printf("%s ", name);
#if defined(CQL_AMALGAM_LEAN) && !defined(CQL_AMALGAM_SEM)
// with no SEM we can't do this conversion, we're just doing vanilla echos
gen_data_type(data_type);
#else
if (gen_callbacks && gen_callbacks->long_to_int_conv && def->sem && (def->sem->sem_type & SEM_TYPE_AUTOINCREMENT)) {
// semantic checking must have already validated that this is either an integer or long_integer
sem_t core_type = core_type_of(def->sem->sem_type);
Contract(core_type == SEM_TYPE_INTEGER || core_type == SEM_TYPE_LONG_INTEGER);
gen_printf("INTEGER");
}
else {
gen_data_type(data_type);
}
#endif
gen_col_attrs(attrs);
}
cql_export bool_t eval_star_callback(ast_node *ast) {
Contract(is_ast_star(ast) || is_ast_table_star(ast));
bool_t suppress = 0;
if (gen_callbacks && gen_callbacks->star_callback && ast->sem) {
CHARBUF_OPEN(buf);
suppress = gen_callbacks->star_callback(ast, gen_callbacks->star_context, &buf);
gen_printf("%s", buf.ptr);
CHARBUF_CLOSE(buf);
}
return suppress;
}
cql_noexport bool_t eval_column_callback(ast_node *ast) {
Contract(is_ast_col_def(ast));
bool_t suppress = 0;
if (gen_callbacks && gen_callbacks->col_def_callback && ast->sem) {
CHARBUF_OPEN(buf);
suppress = gen_callbacks->col_def_callback(ast, gen_callbacks->col_def_context, &buf);
gen_printf("%s", buf.ptr);
CHARBUF_CLOSE(buf);
}
return suppress;
}
#if defined(CQL_AMALGAM_LEAN) && !defined(CQL_AMALGAM_SEM)
// if SEM isn't in the picture there are no "variables"
bool_t eval_variables_callback(ast_node *ast) {
return false;
}
#else
bool_t eval_variables_callback(ast_node *ast) {
bool_t suppress = 0;
if (gen_callbacks && gen_callbacks->variables_callback && ast->sem && is_variable(ast->sem->sem_type)) {
CHARBUF_OPEN(buf);
suppress = gen_callbacks->variables_callback(ast, gen_callbacks->variables_context, &buf);
gen_printf("%s", buf.ptr);
CHARBUF_CLOSE(buf);
}
return suppress;
}
#endif
cql_noexport void gen_col_or_key(ast_node *def) {
if (is_ast_col_def(def)) {
gen_col_def(def);
} else if (is_ast_pk_def(def)) {
gen_pk_def(def);
} else if (is_ast_fk_def(def)) {
gen_fk_def(def);
} else if (is_ast_like(def)) {
gen_shape_def(def);
} else if (is_ast_check_def(def)) {
gen_check_def(def);
} else {
Contract(is_ast_unq_def(def));
gen_unq_def(def);
}
}
cql_noexport void gen_col_key_list(ast_node *list) {
Contract(is_ast_col_key_list(list));
bool_t need_comma = 0;
BEGIN_INDENT(coldefs, 2);
for (ast_node *item = list; item; item = item->right) {
EXTRACT_ANY_NOTNULL(def, item->left);
// give the callback system a chance to suppress columns that are not in this version
if (is_ast_col_def(def) && eval_column_callback(def)) {
continue;
}
if (need_comma) {
gen_printf(",\n");
}
need_comma = 1;
gen_col_or_key(def);
}
END_INDENT(coldefs);
}
static void gen_select_opts(ast_node *ast) {
Contract(is_ast_select_opts(ast));
EXTRACT_ANY_NOTNULL(opt, ast->left);
if (is_ast_all(opt)) {
gen_printf(" ALL");
}
else if (is_ast_distinct(opt)) {
gen_printf(" DISTINCT");
}
else {
Contract(is_ast_distinctrow(opt));
gen_printf(" DISTINCTROW");
}
}
static void gen_binary(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
// We add parens if our priority is less than the parent priority
// meaning something like this:
// * we're a + node, our parent is a * node
// * we need parens because the tree specifies that the + happens before the *
//
// Also, grouping of equal operators is left to right
// so for so if our right child is the same precedence as us
// that means there were parens there in the original expression
// e.g. 3+(4+7);
// effectively it's like we're one binding strength higher for our right child
// so we call it with pri_new + 1. If it's equal to us it must emit parens
if (pri_new < pri) gen_printf("(");
gen_expr(ast->left, pri_new);
gen_printf(" %s ", op);
gen_expr(ast->right, pri_new + 1);
if (pri_new < pri) gen_printf(")");
}
static void gen_unary(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
if (pri_new < pri) gen_printf("(");
gen_printf("%s", op);
gen_expr(ast->left, pri_new);
if (pri_new < pri) gen_printf(")");
}
static void gen_postfix(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
if (pri_new < pri) gen_printf("(");
gen_expr(ast->left, pri_new);
gen_printf(" %s", op);
if (pri_new < pri) gen_printf(")");
}
static void gen_expr_const(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
gen_printf("CONST(");
gen_expr(ast->left, pri_new);
gen_printf(")");
}
static void gen_uminus(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
if (pri_new < pri) gen_printf("(");
gen_printf("%s", op);
// we don't ever want -- in the output because that's a comment
CHARBUF_OPEN(tmp);
charbuf *saved = output;
output = &tmp;
gen_expr(ast->left, pri_new);
output = saved;
if (tmp.ptr[0] == '-') {
gen_printf(" ");
}
gen_printf("%s", tmp.ptr);
CHARBUF_CLOSE(tmp);
if (pri_new < pri) gen_printf(")");
}
static void gen_concat(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
Contract(is_ast_concat(ast));
if (pri_new < pri) gen_printf("(");
gen_expr(ast->left, pri_new);
gen_printf(" %s ", op);
gen_expr(ast->right, pri_new);
if (pri_new < pri) gen_printf(")");
}
static void gen_arg_expr(ast_node *ast) {
if (is_ast_star(ast)) {
gen_printf("*");
}
else if (is_ast_from_shape(ast)) {
gen_shape_arg(ast);
}
else {
gen_root_expr(ast);
}
}
static void gen_expr_exists(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
Contract(is_ast_exists_expr(ast));
EXTRACT_NOTNULL(select_stmt, ast->left);
gen_printf("EXISTS (");
gen_select_stmt(select_stmt);
gen_printf(")");
}
static void gen_arg_list(ast_node *ast) {
while (ast) {
gen_arg_expr(ast->left);
if (ast->right) {
gen_printf(", ");
}
ast = ast->right;
}
}
static void gen_expr_list(ast_node *ast) {
while (ast) {
gen_root_expr(ast->left);
if (ast->right) {
gen_printf(", ");
}
ast = ast->right;
}
}
static void gen_shape_arg(ast_node *ast) {
Contract(is_ast_from_shape(ast));
EXTRACT_STRING(shape, ast->left);
gen_printf("FROM %s", shape);
if (ast->right) {
gen_printf(" ");
gen_shape_def(ast->right);
}
}
static void gen_call_expr_list(ast_node *ast) {
while (ast) {
ast_node *left = ast->left;
if (is_ast_from_shape(left)) {
gen_shape_arg(left);
}
else {
gen_root_expr(left);
}
if (ast->right) {
gen_printf(", ");
}
ast = ast->right;
}
}
static void gen_case_list(ast_node *ast) {
Contract(is_ast_case_list(ast));
while (ast) {
EXTRACT_NOTNULL(when, ast->left);
EXTRACT_ANY_NOTNULL(case_expr, when->left);
EXTRACT_ANY_NOTNULL(then_expr, when->right);
// additional parens never needed because WHEN/THEN act like parens
gen_printf("WHEN ");
gen_root_expr(case_expr);
gen_printf(" THEN ");
gen_root_expr(then_expr);
gen_printf("\n");
ast = ast->right;
}
}
static void gen_expr_num(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
Contract(is_ast_num(ast));
EXTRACT_NUM_VALUE(val, ast);
EXTRACT_NUM_TYPE(num_type, ast);
Contract(val);
if (has_hex_prefix(val) && gen_callbacks && gen_callbacks->convert_hex) {
int64_t v = strtol(val, NULL, 16);
gen_printf("%lld", (llint_t)v);
}
else {
if (for_sqlite() || num_type != NUM_BOOL) {
gen_printf("%s", val);
}
else {
if (!strcmp(val, "0")) {
gen_printf("FALSE", val);
}
else {
gen_printf("TRUE", val);
}
}
}
if (for_sqlite()) {
return;
}
if (num_type == NUM_LONG) {
gen_printf("L");
}
}
static void gen_expr_blob(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
Contract(is_ast_blob(ast));
EXTRACT_BLOBTEXT(str, ast);
// blob literals are easy, we just emit them, there's no conversion or anything like that
gen_printf("%s", str);
}
static void gen_expr_str(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
Contract(is_ast_str(ast));
EXTRACT_STRING(str, ast);
if (is_strlit(ast)) {
str_ast_node *asts = (str_ast_node *)ast;
if (!asts->cstr_literal || for_sqlite()) {
// Note: str is the lexeme, so it is either still quoted and escaped
// or if it was a c string literal it was already normalized to SQL form.
// In both cases we can just print.
gen_printf("%s", str);
}
else {
// If was originally a c string literal re-encode it for echo output
// so that it looks the way it was given to us. This is so that when we
// echo the SQL back for say test output C string literal forms come out
// just as they were given to us.
CHARBUF_OPEN(decoded);
CHARBUF_OPEN(encoded);
cg_decode_string_literal(str, &decoded);
cg_encode_c_string_literal(decoded.ptr, &encoded);
gen_printf("%s", encoded.ptr);
CHARBUF_CLOSE(encoded);
CHARBUF_CLOSE(decoded);
}
}
else {
if (!eval_variables_callback(ast)) {
gen_printf("%s", str); // an identifier
}
}
}
static void gen_expr_null(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
Contract(is_ast_null(ast));
gen_printf("NULL");
}
static void gen_expr_dot(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
Contract(is_ast_dot(ast));
if (eval_variables_callback(ast)) {
return;
}
EXTRACT_STRING(left, ast->left);
EXTRACT_STRING(right, ast->right);
#if defined(CQL_AMALGAM_LEAN) && !defined(CQL_AMALGAM_SEM)
// simple case if SEM is not available
gen_printf("%s.%s", left, right);
#else
if (!strcmp("ARGUMENTS", left) && ast->sem && ast->sem->name) {
// special case for rewritten arguments, hide the "ARGUMENTS." stuff
gen_printf("%s", ast->sem->name);
}
else {
gen_printf("%s.%s", left, right);
}
#endif
}
static void gen_expr_in_pred(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
Contract(is_ast_in_pred(ast));
if (pri_new < pri) gen_printf("(");
gen_expr(ast->left, pri_new);
gen_printf(" IN (");
if (is_ast_expr_list(ast->right)) {
EXTRACT_NOTNULL(expr_list, ast->right);
gen_expr_list(expr_list);
}
else {
EXTRACT_ANY_NOTNULL(select_stmt, ast->right);
gen_select_stmt(select_stmt);
}
gen_printf(")");
if (pri_new < pri) gen_printf(")");
}
static void gen_expr_not_in(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
Contract(is_ast_not_in(ast));
if (pri_new < pri) gen_printf("(");
gen_expr(ast->left, pri_new);
gen_printf(" NOT IN (");
if (is_ast_expr_list(ast->right)) {
EXTRACT_NOTNULL(expr_list, ast->right);
gen_expr_list(expr_list);
}
else {
EXTRACT_ANY_NOTNULL(select_stmt, ast->right);
gen_select_stmt(select_stmt);
}
gen_printf(")");
if (pri_new < pri) gen_printf(")");
}
static void gen_expr_call(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
Contract(is_ast_call(ast));
EXTRACT_ANY_NOTNULL(name_ast, ast->left);
EXTRACT_STRING(name, name_ast);
EXTRACT_NOTNULL(call_arg_list, ast->right);
EXTRACT_NOTNULL(call_filter_clause, call_arg_list->left);
EXTRACT(distinct, call_filter_clause->left);
EXTRACT(opt_filter_clause, call_filter_clause->right);
EXTRACT(arg_list, call_arg_list->right);
// We never want this to appear. Calls to `cql_inferred_notnull` exist only as
// the product of a rewrite rule and should not be visible to users.
if (!Strcasecmp("cql_inferred_notnull", name)) {
gen_arg_list(arg_list);
return;
}
if (for_sqlite()) {
// These functions are all no-ops in SQL and must not be emitted if we're
// doing codegen: They're only present within queries in source programs for
// the purpose of manipulating types.
if (!Strcasecmp("nullable", name)) {
gen_arg_list(arg_list);
return;
}
if (!Strcasecmp("ptr", name)) {
gen_arg_list(arg_list);
return;
}
if (!Strcasecmp("sensitive", name)) {
gen_arg_list(arg_list);
return;
}
bool_t has_inline_func_callback = gen_callbacks && gen_callbacks->inline_func_callback;
if (has_inline_func_callback) {
if (ast->left && ast->left->sem && (ast->left->sem->sem_type & SEM_TYPE_INLINE_CALL)) {
bool_t handled = gen_callbacks->inline_func_callback(ast, gen_callbacks->inline_func_context, output);
if (handled) {
return;
}
}
}
}
gen_printf("%s(", name);
if (distinct) {
gen_printf("DISTINCT ");
}
gen_arg_list(arg_list);
gen_printf(")");
if (opt_filter_clause) {
gen_opt_filter_clause(opt_filter_clause);
}
}
static void gen_opt_filter_clause(ast_node *ast) {
Contract(is_ast_opt_filter_clause(ast));
EXTRACT_NOTNULL(opt_where, ast->left);
gen_printf(" FILTER (");
gen_opt_where_without_new_line(opt_where);
gen_printf(")");
}
static void gen_opt_partition_by(ast_node *ast) {
Contract(is_ast_opt_partition_by(ast));
EXTRACT_NOTNULL(expr_list, ast->left);
gen_printf("PARTITION BY ");
gen_expr_list(expr_list);
}
static void gen_frame_spec_flags(int32_t flags) {
if (flags & FRAME_TYPE_RANGE) {
gen_printf("RANGE");
}
if (flags & FRAME_TYPE_ROWS) {
gen_printf("ROWS");
}
if (flags & FRAME_TYPE_GROUPS) {
gen_printf("GROUPS");
}
if (flags & FRAME_BOUNDARY_UNBOUNDED || flags & FRAME_BOUNDARY_START_UNBOUNDED) {
gen_printf("UNBOUNDED PRECEDING");
}
if (flags & FRAME_BOUNDARY_PRECEDING ||
flags & FRAME_BOUNDARY_START_PRECEDING ||
flags & FRAME_BOUNDARY_END_PRECEDING) {
gen_printf("PRECEDING");
}
if (flags & FRAME_BOUNDARY_CURRENT_ROW ||
flags & FRAME_BOUNDARY_START_CURRENT_ROW ||
flags & FRAME_BOUNDARY_END_CURRENT_ROW) {
gen_printf("CURRENT ROW");
}
if (flags & FRAME_BOUNDARY_START_FOLLOWING ||
flags & FRAME_BOUNDARY_END_FOLLOWING) {
gen_printf("FOLLOWING");
}
if (flags & FRAME_BOUNDARY_END_UNBOUNDED) {
gen_printf("UNBOUNDED FOLLOWING");
}
if (flags & FRAME_EXCLUDE_NO_OTHERS) {
gen_printf("EXCLUDE NO OTHERS");
}
if (flags & FRAME_EXCLUDE_CURRENT_ROW) {
gen_printf("EXCLUDE CURRENT ROW");
}
if (flags & FRAME_EXCLUDE_GROUP) {
gen_printf("EXCLUDE GROUP");
}
if (flags & FRAME_EXCLUDE_TIES) {
gen_printf("EXCLUDE TIES");
}
}
static void gen_frame_type(int32_t flags) {
Invariant(flags == (flags & FRAME_TYPE_FLAGS));
gen_frame_spec_flags(flags);
gen_printf(" ");
}
static void gen_frame_exclude(int32_t flags) {
Invariant(flags == (flags & FRAME_EXCLUDE_FLAGS));
if (flags != FRAME_EXCLUDE_NONE) {
gen_printf(" ");
}
gen_frame_spec_flags(flags);
}
static void gen_frame_boundary(ast_node *ast, int32_t flags) {
EXTRACT_ANY(expr, ast->left);
Invariant(flags == (flags & FRAME_BOUNDARY_FLAGS));
if (expr) {
gen_root_expr(expr);
gen_printf(" ");
}
gen_frame_spec_flags(flags);
}
static void gen_frame_boundary_start(ast_node *ast, int32_t flags) {
Contract(is_ast_expr_list(ast));
EXTRACT_ANY(expr, ast->left);
Invariant(flags == (flags & FRAME_BOUNDARY_START_FLAGS));
gen_printf("BETWEEN ");
if (expr) {
gen_root_expr(expr);
gen_printf(" ");
}
gen_frame_spec_flags(flags);
}
static void gen_frame_boundary_end(ast_node *ast, int32_t flags) {
Contract(is_ast_expr_list(ast));
EXTRACT_ANY(expr, ast->right);
Invariant(flags == (flags & FRAME_BOUNDARY_END_FLAGS));
gen_printf(" AND ");
if (expr) {
gen_root_expr(expr);
gen_printf(" ");
}
gen_frame_spec_flags(flags);
}
static void gen_opt_frame_spec(ast_node *ast) {
Contract(is_ast_opt_frame_spec(ast));
EXTRACT_OPTION(flags, ast->left);
EXTRACT_NOTNULL(expr_list, ast->right);
int32_t frame_type_flags = flags & FRAME_TYPE_FLAGS;
int32_t frame_boundary_flags = flags & FRAME_BOUNDARY_FLAGS;
int32_t frame_boundary_start_flags = flags & FRAME_BOUNDARY_START_FLAGS;
int32_t frame_boundary_end_flags = flags & FRAME_BOUNDARY_END_FLAGS;
int32_t frame_exclude_flags = flags & FRAME_EXCLUDE_FLAGS;
if (frame_type_flags) {
gen_frame_type(frame_type_flags);
}
if (frame_boundary_flags) {
gen_frame_boundary(expr_list, frame_boundary_flags);
}
if (frame_boundary_start_flags) {
gen_frame_boundary_start(expr_list, frame_boundary_start_flags);
}
if (frame_boundary_end_flags) {
gen_frame_boundary_end(expr_list, frame_boundary_end_flags);
}
if (frame_exclude_flags) {
gen_frame_exclude(frame_exclude_flags);
}
}
static void gen_window_defn(ast_node *ast) {
Contract(is_ast_window_defn(ast));
EXTRACT(opt_partition_by, ast->left);
EXTRACT_NOTNULL(window_defn_orderby, ast->right);
EXTRACT(opt_orderby, window_defn_orderby->left);
EXTRACT(opt_frame_spec, window_defn_orderby->right);
// the first optional element never needs a space
bool need_space = 0;
gen_printf(" (");
if (opt_partition_by) {
Invariant(!need_space);
gen_opt_partition_by(opt_partition_by);
need_space = 1;
}
if (opt_orderby) {
if (need_space) gen_printf(" ");
gen_opt_orderby(opt_orderby);
need_space = 1;
}
if (opt_frame_spec) {
if (need_space) gen_printf(" ");
gen_opt_frame_spec(opt_frame_spec);
}
gen_printf(")");
}
static void gen_name_or_window_defn(ast_node *ast) {
if (is_ast_str(ast)) {
EXTRACT_STRING(window_name, ast);
gen_printf(" %s", window_name);
}
else {
Contract(is_ast_window_defn(ast));
gen_window_defn(ast);
}
}
static void gen_expr_window_func_inv(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
Contract(is_ast_window_func_inv(ast));
EXTRACT_NOTNULL(call, ast->left);
EXTRACT_ANY_NOTNULL(name_or_window_defn, ast->right);
gen_printf("\n ");
gen_expr_call(call, op, pri, pri_new);
gen_printf(" OVER");
gen_name_or_window_defn(name_or_window_defn);
}
static void gen_expr_raise(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
Contract(is_ast_raise(ast));
EXTRACT_OPTION(flags, ast->left);
EXTRACT_ANY(expr, ast->right);
Contract(flags >= RAISE_IGNORE && flags <= RAISE_FAIL);
gen_printf("RAISE(");
switch (flags) {
case RAISE_IGNORE: gen_printf("IGNORE"); break;
case RAISE_ROLLBACK: gen_printf("ROLLBACK"); break;
case RAISE_ABORT: gen_printf("ABORT"); break;
case RAISE_FAIL: gen_printf("FAIL"); break;
}
if (expr) {
gen_printf(", ");
gen_root_expr(expr);
}
gen_printf(")");
}
static void gen_expr_between(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
Contract(is_ast_between(ast));
EXTRACT_NOTNULL(range, ast->right);
if (pri_new < pri) gen_printf("(");
gen_expr(ast->left, pri_new);
gen_printf(" BETWEEN ");
gen_expr(range->left, pri_new);
gen_printf(" AND ");
gen_expr(range->right, pri_new + 1); // the usual rules for the right operand (see gen_binary)
if (pri_new < pri) gen_printf(")");
}
static void gen_expr_not_between(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
Contract(is_ast_not_between(ast));
EXTRACT_NOTNULL(range, ast->right);
if (pri_new < pri) gen_printf("(");
gen_expr(ast->left, pri_new);
gen_printf(" NOT BETWEEN ");
gen_expr(range->left, pri_new);
gen_printf(" AND ");
gen_expr(range->right, pri_new + 1); // the usual rules for the right operand (see gen_binary)
if (pri_new < pri) gen_printf(")");
}
static void gen_expr_between_rewrite(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
Contract(is_ast_between_rewrite(ast));
EXTRACT_NOTNULL(range, ast->right);
if (pri_new < pri) gen_printf("(");
gen_printf("BETWEEN REWRITE ");
gen_expr(range->left, pri_new);
gen_printf(" := ");
gen_expr(ast->left, pri_new);
gen_printf(" CHECK ");
gen_expr(range->right, pri_new);
if (pri_new < pri) gen_printf(")");
}
static void gen_expr_case(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
Contract(is_ast_case_expr(ast));
EXTRACT_ANY(expr, ast->left);
EXTRACT_NOTNULL(connector, ast->right);
EXTRACT_NOTNULL(case_list, connector->left);
EXTRACT_ANY(else_expr, connector->right);
// case is like parens already, you never need more parens
gen_printf("CASE ");
if (expr) {
// case can have expression or just when clauses
gen_root_expr(expr);
gen_printf(" ");
}
gen_case_list(case_list);
if (else_expr) {
gen_printf("ELSE ");
gen_root_expr(else_expr);
gen_printf("\n");
}
gen_printf("END");
}
static void gen_expr_select(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
Contract(is_select_stmt(ast));
gen_printf("( ");
gen_select_stmt(ast);
gen_printf(" )");
}
static void gen_expr_select_if_nothing_throw(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
Contract(is_ast_select_if_nothing_throw_expr(ast));
EXTRACT_ANY_NOTNULL(select_stmt, ast->left);
gen_printf("( ");
gen_select_stmt(select_stmt);
gen_printf(" IF NOTHING THROW )");
}
static void gen_expr_select_if_nothing(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
Contract(is_ast_select_if_nothing_expr(ast) || is_ast_select_if_nothing_or_null_expr(ast));
EXTRACT_ANY_NOTNULL(select_stmt, ast->left);
EXTRACT_ANY_NOTNULL(else_expr, ast->right);
gen_printf("( ");
gen_select_stmt(select_stmt);
gen_printf(" %s ", op);
gen_root_expr(else_expr);
gen_printf(" )");
}
static void gen_expr_cast(ast_node *ast, CSTR op, int32_t pri, int32_t pri_new) {
Contract(is_ast_cast_expr(ast));
EXTRACT_ANY_NOTNULL(expr, ast->left);
EXTRACT_ANY_NOTNULL(data_type, ast->right);
if (gen_callbacks && gen_callbacks->minify_casts) {
if (is_ast_null(expr)) {
// when generating the actual SQL for Sqlite, we don't need to include cast expressions on NULL
// we only need those for type checking.
gen_printf("NULL");
return;
}
#if defined(CQL_AMALGAM_LEAN) && !defined(CQL_AMALGAM_SEM)
// with no SEM we can't do optimization, nor is there any need
#else
if (expr->sem && ast->sem) {
// If the expression is already of the correct type (less nullability), we don't need the cast at all.
sem_t core_type_expr = core_type_of(expr->sem->sem_type);
sem_t core_type_ast = core_type_of(ast->sem->sem_type);
if (core_type_expr == core_type_ast) {
gen_printf("(");
gen_expr(expr, EXPR_PRI_ROOT);
gen_printf(")");
return;
}
}
#endif
}
gen_printf("CAST(");
gen_expr(expr, EXPR_PRI_ROOT);
gen_printf(" AS ");
gen_data_type(data_type);
gen_printf(")");
}
static void gen_expr(ast_node *ast, int32_t pri) {
// These are all the expressions there are, we have to find it in this table
// or else someone added a new expression type and it isn't supported yet.
symtab_entry *entry = symtab_find(gen_exprs, ast->type);
Invariant(entry);
gen_expr_dispatch *disp = (gen_expr_dispatch*)entry->val;
disp->func(ast, disp->str, pri, disp->pri_new);
}
cql_noexport void gen_root_expr(ast_node *ast) {
gen_expr(ast, EXPR_PRI_ROOT);
}
static void gen_as_alias(ast_node *ast) {
EXTRACT_STRING(name, ast->left);
gen_printf(" AS %s", name);
}
static void gen_select_expr(ast_node *ast) {
Contract(is_ast_select_expr(ast));
EXTRACT_ANY_NOTNULL(expr, ast->left);
EXTRACT(opt_as_alias, ast->right);
gen_root_expr(expr);
if (opt_as_alias) {
EXTRACT_STRING(name, opt_as_alias->left);
if (used_alias_syms && !symtab_find(used_alias_syms, name)) {
return;
}
gen_as_alias(opt_as_alias);
}
}
static void gen_col_calc(ast_node *ast) {
Contract(is_ast_col_calc(ast));
if (ast->left) {
EXTRACT_NAME_AND_SCOPE(ast->left);
if (scope) {
gen_printf("%s.%s", scope, name);
} else {
gen_printf("%s", name);
}
if (ast->right) {
gen_printf(" ");
}
}
if (ast->right) {
gen_shape_def(ast->right);
}
}
static void gen_col_calcs(ast_node *ast) {
Contract(is_ast_col_calcs(ast));
ast_node *item = ast;
while (item) {
gen_col_calc(item->left);
if (item->right) {
gen_printf(", ");
}
item = item->right;
}
}
static void gen_column_calculation(ast_node *ast) {
Contract(is_ast_column_calculation(ast));
gen_printf("COLUMNS(");
if (ast->right) {
gen_printf("DISTINCT ");
}
gen_col_calcs(ast->left);
gen_printf(")");
}
static void gen_select_expr_list(ast_node *ast) {
if (is_ast_star(ast->left)) {
Contract(ast->right == NULL);
if (!eval_star_callback(ast->left)) {
gen_printf("*");
}
return;
}
symtab *temp = used_alias_syms;
used_alias_syms = NULL;
#if defined(CQL_AMALGAM_LEAN) && !defined(CQL_AMALGAM_SEM)
// if there is no SEM then we can't do this minificiation
#else
if (ast->sem && gen_callbacks && gen_callbacks->minify_aliases) {
used_alias_syms = ast->sem->used_symbols;
}
#endif
for (ast_node *item = ast; item; item = item->right) {
ast_node *expr = item->left;
if (is_ast_table_star(expr)) {
if (!eval_star_callback(expr)) {
EXTRACT_NOTNULL(table_star, expr);
EXTRACT_STRING(name, table_star->left);
gen_printf("%s.*", name);
}
}
else if (is_ast_column_calculation(expr)) {
gen_column_calculation(expr);
}
else {
EXTRACT_NOTNULL(select_expr, expr);
gen_select_expr(select_expr);
}
if (item->right) {
gen_printf(", ");
}
}
used_alias_syms = temp;
}
static void gen_table_or_subquery(ast_node *ast) {
Contract(is_ast_table_or_subquery(ast));
EXTRACT_ANY_NOTNULL(factor, ast->left);
if (is_ast_str(factor)) {
EXTRACT_STRING(name, factor);
bool_t has_table_rename_callback = gen_callbacks && gen_callbacks->table_rename_callback;
bool_t handled = false;
if (has_table_rename_callback) {
handled = gen_callbacks->table_rename_callback(factor, gen_callbacks->table_rename_context, output);
}
if (!handled) {
gen_printf("%s", name);
}
}
else if (is_ast_select_stmt(factor) || is_ast_with_select_stmt(factor)) {
gen_printf("(");
gen_select_stmt(factor);
gen_printf(")");
}
else if (is_ast_shared_cte(factor)) {
gen_printf("(");
gen_shared_cte(factor);
gen_printf(")");
}
else if (is_ast_table_function(factor)) {
EXTRACT_STRING(name, factor->left);
EXTRACT(arg_list, factor->right);
gen_printf("%s(", name);
gen_arg_list(arg_list);
gen_printf(")");
}
else {
// this is all that's left
gen_printf("(");
gen_query_parts(factor);
gen_printf(")");
}
EXTRACT(opt_as_alias, ast->right);
if (opt_as_alias) {
gen_as_alias(opt_as_alias);
}
}
static void gen_join_cond(ast_node *ast) {
Contract(is_ast_join_cond(ast));
EXTRACT_ANY_NOTNULL(cond_type, ast->left);
if (is_ast_on(cond_type)) {
gen_printf(" ON ");
gen_root_expr(ast->right);
}
else {
// only other ast type that is allowed
Contract(is_ast_using(cond_type));
gen_printf(" USING (");
gen_name_list(ast->right);
gen_printf(")");
}
}
static void gen_join_target(ast_node *ast) {
Contract(is_ast_join_target(ast));
EXTRACT_OPTION(join_type, ast->left);
switch (join_type) {
case JOIN_INNER: gen_printf("\n INNER JOIN "); break;
case JOIN_CROSS: gen_printf("\n CROSS JOIN "); break;
case JOIN_LEFT_OUTER: gen_printf("\n LEFT OUTER JOIN "); break;
case JOIN_RIGHT_OUTER: gen_printf("\n RIGHT OUTER JOIN "); break;
case JOIN_LEFT: gen_printf("\n LEFT JOIN "); break;
case JOIN_RIGHT: gen_printf("\n RIGHT JOIN "); break;
}
EXTRACT_NOTNULL(table_join, ast->right);
EXTRACT_NOTNULL(table_or_subquery, table_join->left);
gen_table_or_subquery(table_or_subquery);
EXTRACT(join_cond, table_join->right);
if (join_cond) {
gen_join_cond(join_cond);
}
}
static void gen_join_target_list(ast_node *ast) {
Contract(is_ast_join_target_list(ast));
for (ast_node *item = ast; item; item = item->right) {
EXTRACT(join_target, item->left);
gen_join_target(join_target);
}
}
static void gen_join_clause(ast_node *ast) {
Contract(is_ast_join_clause(ast));
EXTRACT_NOTNULL(table_or_subquery, ast->left);
EXTRACT_NOTNULL(join_target_list, ast->right);
gen_table_or_subquery(table_or_subquery);
gen_join_target_list(join_target_list);
}
static void gen_table_or_subquery_list(ast_node *ast) {
Contract(is_ast_table_or_subquery_list(ast));
for (ast_node *item = ast; item; item = item->right) {
gen_table_or_subquery(item->left);
if (item->right) {
gen_printf(",\n");
}
}
}
static void gen_query_parts(ast_node *ast) {
if (is_ast_table_or_subquery_list(ast)) {
gen_table_or_subquery_list(ast);
}
else {
Contract(is_ast_join_clause(ast)); // this is the only other choice
gen_join_clause(ast);
}
}
static void gen_asc_desc(ast_node *ast) {
if (is_ast_asc(ast)) {
gen_printf(" ASC");
}
else if (is_ast_desc(ast)) {
gen_printf(" DESC");
}
else {
Contract(!ast);
}
}
static void gen_groupby_list(ast_node *ast) {
Contract(is_ast_groupby_list(ast));
for (ast_node *item = ast; item; item = item->right) {
Contract(is_ast_groupby_list(item));
EXTRACT_NOTNULL(groupby_item, item->left);
EXTRACT_ANY_NOTNULL(expr, groupby_item->left);
gen_root_expr(expr);
if (item->right) {
gen_printf(", ");
}
}
}
static void gen_orderby_list(ast_node *ast) {
Contract(is_ast_orderby_list(ast));
for (ast_node *item = ast; item; item = item->right) {
Contract(is_ast_orderby_list(item));
EXTRACT_NOTNULL(orderby_item, item->left);
EXTRACT_ANY_NOTNULL(expr, orderby_item->left);
EXTRACT_ANY(opt_asc_desc, orderby_item->right);
gen_root_expr(expr);
gen_asc_desc(opt_asc_desc);
if (item->right) {
gen_printf(", ");
}
}
}
static void gen_opt_where(ast_node *ast) {
gen_printf("\n ");
gen_opt_where_without_new_line(ast);
}
static void gen_opt_where_without_new_line(ast_node *ast) {
Contract(is_ast_opt_where(ast));
gen_printf("WHERE ");
gen_root_expr(ast->left);
}
static void gen_opt_groupby(ast_node *ast) {
Contract(is_ast_opt_groupby(ast));
EXTRACT_NOTNULL(groupby_list, ast->left);
gen_printf("\n GROUP BY ");
gen_groupby_list(groupby_list);
}
static void gen_opt_orderby(ast_node *ast) {
Contract(is_ast_opt_orderby(ast));
EXTRACT_NOTNULL(orderby_list, ast->left);
gen_printf("ORDER BY ");
gen_orderby_list(orderby_list);
}
static void gen_opt_limit(ast_node *ast) {
Contract(is_ast_opt_limit(ast));
gen_printf("\nLIMIT ");
gen_root_expr(ast->left);
}
static void gen_opt_offset(ast_node *ast) {
Contract(is_ast_opt_offset(ast));
gen_printf("\nOFFSET ");
gen_root_expr(ast->left);
}
static void gen_window_name_defn(ast_node *ast) {
Contract(is_ast_window_name_defn(ast));
EXTRACT_STRING(name, ast->left);
EXTRACT_NOTNULL(window_defn, ast->right);
gen_printf("\n %s AS", name);
gen_window_defn(window_defn);
}
static void gen_window_name_defn_list(ast_node *ast) {
Contract(is_ast_window_name_defn_list(ast));
for (ast_node *item = ast; item; item = item->right) {
EXTRACT_NOTNULL(window_name_defn, item->left);
gen_window_name_defn(window_name_defn);
if (item->right) {
gen_printf(", ");
}
}
}
static void gen_window_clause(ast_node *ast) {
Contract(is_ast_window_clause(ast));
EXTRACT_NOTNULL(window_name_defn_list, ast->left);
gen_window_name_defn_list(window_name_defn_list);
}
static void gen_opt_select_window(ast_node *ast) {
Contract(is_ast_opt_select_window(ast));
EXTRACT_NOTNULL(window_clause, ast->left);
gen_printf("\n WINDOW ");
gen_window_clause(window_clause);
}
static void gen_select_from_etc(ast_node *ast) {
Contract(is_ast_select_from_etc(ast));
EXTRACT_ANY(query_parts, ast->left);
EXTRACT_NOTNULL(select_where, ast->right);
EXTRACT(opt_where, select_where->left);
EXTRACT_NOTNULL(select_groupby, select_where->right);
EXTRACT(opt_groupby, select_groupby->left);
EXTRACT_NOTNULL(select_having, select_groupby->right);
EXTRACT(opt_having, select_having->left);
EXTRACT(opt_select_window, select_having->right);
if (query_parts) {
gen_printf ("\n FROM ");
gen_query_parts(query_parts);
}
if (opt_where) {
gen_opt_where(opt_where);
}
if (opt_groupby) {
gen_opt_groupby(opt_groupby);
}
if (opt_having) {
gen_printf("\n HAVING ");
gen_root_expr(opt_having->left);
}
if (opt_select_window) {
gen_opt_select_window(opt_select_window);
}
}
static void gen_select_orderby(ast_node *ast) {
Contract(is_ast_select_orderby(ast));
EXTRACT(opt_orderby, ast->left);
EXTRACT_NOTNULL(select_limit, ast->right);
EXTRACT(opt_limit, select_limit->left);
EXTRACT_NOTNULL(select_offset, select_limit->right);
EXTRACT(opt_offset, select_offset->left);
if (opt_orderby) {
gen_printf("\n");
gen_opt_orderby(opt_orderby);
}
if (opt_limit) {
gen_opt_limit(opt_limit);
}
if (opt_offset) {
gen_opt_offset(opt_offset);
}
}
static void gen_select_expr_list_con(ast_node *ast) {
Contract(is_ast_select_expr_list_con(ast));
EXTRACT(select_expr_list, ast->left);
EXTRACT(select_from_etc, ast->right);
gen_select_expr_list(select_expr_list);
if (select_from_etc) {
gen_select_from_etc(select_from_etc);
}
}
cql_noexport void init_gen_sql_callbacks(gen_sql_callbacks *cb)
{
memset((void *)cb, 0, sizeof(*gen_callbacks));
// with callbacks is for SQLite be default, the normal raw output
// case is done with callbacks == NULL
cb->mode = gen_mode_sql;
}
static void gen_select_statement_type(ast_node *ast) {
Contract(is_ast_select_core(ast));
EXTRACT_ANY(select_opts, ast->left);
if (select_opts && is_ast_select_values(select_opts)) {
gen_printf("VALUES");
} else {
gen_printf("SELECT");
if (select_opts) {
Contract(is_ast_select_opts(select_opts));
gen_select_opts(select_opts);
}
}
}
static void gen_values(ast_node *ast) {
Contract(is_ast_values(ast));
for (ast_node *item = ast; item; item = item->right) {
EXTRACT(insert_list, item->left);
gen_printf("(");
if (insert_list) {
gen_insert_list(insert_list);
}
gen_printf(")");
if (item->right) {
gen_printf(", ");
}
}
}
cql_noexport void gen_select_core(ast_node *ast) {
Contract(is_ast_select_core(ast));
EXTRACT_ANY(select_core_left, ast->left);
gen_select_statement_type(ast);
// select_core subtree can be a SELECT or VALUES statement
if (is_ast_select_values(select_core_left)) {
// VALUES [values]
EXTRACT(values, ast->right);
gen_values(values);
} else {
// SELECT [select_expr_list_con]
// We're making sure that we're in the SELECT clause of the select stmt
Contract(select_core_left == NULL || is_ast_select_opts(select_core_left));
gen_printf(" ");
EXTRACT_NOTNULL(select_expr_list_con, ast->right);
gen_select_expr_list_con(select_expr_list_con);
}
}
static void gen_select_no_with(ast_node *ast) {
Contract(is_ast_select_stmt(ast));
EXTRACT_NOTNULL(select_core_list, ast->left);
EXTRACT_NOTNULL(select_orderby, ast->right);
gen_select_core_list(select_core_list);
gen_select_orderby(select_orderby);
}
static void gen_cte_decl(ast_node *ast) {
Contract(is_ast_cte_decl(ast));
EXTRACT_STRING(name, ast->left);
gen_printf("%s (", name);
if (is_ast_star(ast->right)) {
gen_printf("*", name);
}
else {
gen_name_list(ast->right);
}
gen_printf(")", name);
}
static void gen_cte_binding_list(ast_node *ast) {
Contract(is_ast_cte_binding_list(ast));
while (ast) {
EXTRACT_NOTNULL(cte_binding, ast->left);
EXTRACT_STRING(actual, cte_binding->left);
EXTRACT_STRING(formal, cte_binding->right);
gen_printf("%s AS %s", actual, formal);
if (ast->right) {
gen_printf(", ");
}
ast = ast->right;
}
}
static void gen_shared_cte(ast_node *ast) {
Contract(is_ast_shared_cte(ast));
bool_t has_cte_procs_callback = gen_callbacks && gen_callbacks->cte_proc_callback;
bool_t handled = false;
if (has_cte_procs_callback) {
handled = gen_callbacks->cte_proc_callback(ast, gen_callbacks->cte_proc_context, output);
}
if (!handled) {
EXTRACT_NOTNULL(call_stmt, ast->left);
EXTRACT(cte_binding_list, ast->right);
gen_call_stmt(call_stmt);
if (cte_binding_list) {
gen_printf(" USING ");
gen_cte_binding_list(cte_binding_list);
}
}
}
static void gen_cte_table(ast_node *ast) {
Contract(is_ast_cte_table(ast));
EXTRACT(cte_decl, ast->left);
EXTRACT_ANY_NOTNULL(cte_body, ast->right);
gen_cte_decl(cte_decl);
if (is_ast_like(cte_body)) {
gen_printf(" LIKE ");
if (is_ast_str(cte_body->left)) {
gen_name(cte_body->left);
}
else {
gen_printf("(");
gen_select_stmt(cte_body->left);
gen_printf(")");
}
return;
}
gen_printf(" AS (");
if (is_ast_shared_cte(cte_body)) {
gen_shared_cte(cte_body);
}
else {
// the only other alternative is the select statement form
gen_select_stmt(cte_body);
}
gen_printf(")");
}
static void gen_cte_tables(ast_node *ast, CSTR prefix) {
Contract(is_ast_cte_tables(ast));
bool_t first = true;
while (ast) {
EXTRACT_NOTNULL(cte_table, ast->left);
// callbacks can suppress some CTE for use in shared_fragments
bool_t has_cte_suppress_callback = gen_callbacks && gen_callbacks->cte_suppress_callback;
bool_t handled = false;
if (has_cte_suppress_callback) {
handled = gen_callbacks->cte_suppress_callback(cte_table, gen_callbacks->cte_suppress_context, output);
}
if (!handled) {
if (first) {
gen_printf("%s", prefix);
first = false;
}
else {
gen_printf(",\n");
}
gen_cte_table(cte_table);
}
ast = ast->right;
}
if (!first) {
gen_printf("\n");
}
}
static void gen_with_prefix(ast_node *ast) {
EXTRACT(cte_tables, ast->left);
CSTR prefix;
// for us there is no difference between WITH and WITH RECURSIVE
// except we have to remember which one it was so that we can
// emit the same thing we saw. Sqlite lets you do recursion
// even if don't use WITH RECURSIVE
if (is_ast_with(ast)) {
prefix = "WITH\n";
}
else {
Contract(is_ast_with_recursive(ast));
prefix = "WITH RECURSIVE\n";
}
gen_cte_tables(cte_tables, prefix);
}
static void gen_with_select_stmt(ast_node *ast) {
Contract(is_ast_with_select_stmt(ast));
EXTRACT_ANY_NOTNULL(with_prefix, ast->left)
EXTRACT_ANY_NOTNULL(select_stmt, ast->right);
gen_with_prefix(with_prefix);
gen_select_stmt(select_stmt);
}
static void gen_select_core_list(ast_node *ast) {
Contract(is_ast_select_core_list(ast));
EXTRACT_NOTNULL(select_core, ast->left);
gen_select_core(select_core);
EXTRACT(select_core_compound, ast->right);
if (!select_core_compound) {
return;
}
EXTRACT_OPTION(compound_operator, select_core_compound->left);
EXTRACT_NOTNULL(select_core_list, select_core_compound->right);
gen_printf("\n%s\n", get_compound_operator_name(compound_operator));
gen_select_core_list(select_core_list);
}
static void gen_select_stmt(ast_node *ast) {
if (is_ast_with_select_stmt(ast)) {
gen_with_select_stmt(ast);
}
else {
Contract(is_ast_select_stmt(ast));
gen_select_no_with(ast);
}
}
static void gen_version_attrs(ast_node *_Nullable ast) {
for (ast_node *attr = ast; attr; attr = attr->right) {
if (is_ast_recreate_attr(attr)) {
gen_recreate_attr(attr);
}
else if (is_ast_create_attr(attr)) {
gen_create_attr(attr);
} else {
Contract(is_ast_delete_attr(attr)); // the only other kind
gen_delete_attr(attr);
}
}
}
// If there is a handler, the handler will decide what to do. If there is no handler
// or the handler returns false, then we honor the flag bit. This lets you override
// the if_not_exists flag forcing it to be either ignored or enabled. Both are potentially
// needed. When emitting schema creation scripts for instance we always use IF NOT EXISTS
// even if the schema declaration didn't have it (which it usually doesn't).
static void gen_if_not_exists(ast_node *ast, bool_t if_not_exist) {
bool_t if_not_exists_callback = gen_callbacks && gen_callbacks->if_not_exists_callback;
bool_t handled = false;
if (if_not_exists_callback) {
handled = gen_callbacks->if_not_exists_callback(ast, gen_callbacks->if_not_exists_context, output);
}
if (if_not_exist && !handled) {
gen_printf("IF NOT EXISTS ");
}
}
static void gen_eponymous(ast_node *ast, bool_t is_eponymous) {
if (!for_sqlite() && is_eponymous) {
gen_printf("@EPONYMOUS ");
}
}
static void gen_create_view_stmt(ast_node *ast) {
Contract(is_ast_create_view_stmt(ast));
EXTRACT_OPTION(flags, ast->left);
EXTRACT(view_and_attrs, ast->right);
EXTRACT(name_and_select, view_and_attrs->left);
EXTRACT_ANY(attrs, view_and_attrs->right);
EXTRACT_ANY_NOTNULL(select_stmt, name_and_select->right);
EXTRACT_ANY_NOTNULL(name_ast, name_and_select->left);
EXTRACT_STRING(name, name_ast);
bool_t if_not_exist = !!(flags & VIEW_IF_NOT_EXISTS);
gen_printf("CREATE ");
if (flags & VIEW_IS_TEMP) {
gen_printf("TEMP ");
}
gen_printf("VIEW ");
gen_if_not_exists(ast, if_not_exist);
gen_printf("%s AS\n", name);
gen_select_stmt(select_stmt);
gen_version_attrs(attrs);
}
static void gen_create_trigger_stmt(ast_node *ast) {
Contract(is_ast_create_trigger_stmt(ast));
EXTRACT_OPTION(flags, ast->left);
EXTRACT_NOTNULL(trigger_body_vers, ast->right);
EXTRACT_ANY(trigger_attrs, trigger_body_vers->right);
EXTRACT_NOTNULL(trigger_def, trigger_body_vers->left);
EXTRACT_STRING(trigger_name, trigger_def->left);
EXTRACT_NOTNULL(trigger_condition, trigger_def->right);
EXTRACT_OPTION(cond_flags, trigger_condition->left);
flags |= cond_flags;
EXTRACT_NOTNULL(trigger_op_target, trigger_condition->right);
EXTRACT_NOTNULL(trigger_operation, trigger_op_target->left);
EXTRACT_OPTION(op_flags, trigger_operation->left);
EXTRACT(name_list, trigger_operation->right);
flags |= op_flags;
EXTRACT_NOTNULL(trigger_target_action, trigger_op_target->right);
EXTRACT_STRING(table_name, trigger_target_action->left);
EXTRACT_NOTNULL(trigger_action, trigger_target_action->right);
EXTRACT_OPTION(action_flags, trigger_action->left);
flags |= action_flags;
EXTRACT_NOTNULL(trigger_when_stmts, trigger_action->right);
EXTRACT_ANY(when_expr, trigger_when_stmts->left);
EXTRACT_NOTNULL(stmt_list, trigger_when_stmts->right);
gen_printf("CREATE ");
if (flags & TRIGGER_IS_TEMP) {
gen_printf("TEMP ");
}
gen_printf("TRIGGER ");
gen_if_not_exists(ast, !!(flags & TRIGGER_IF_NOT_EXISTS));
gen_printf("%s\n ", trigger_name);
if (flags & TRIGGER_BEFORE) {
gen_printf("BEFORE ");
}
else if (flags & TRIGGER_AFTER) {
gen_printf("AFTER ");
}
else if (flags & TRIGGER_INSTEAD_OF) {
gen_printf("INSTEAD OF ");
}
if (flags & TRIGGER_DELETE) {
gen_printf("DELETE ");
}
else if (flags & TRIGGER_INSERT) {
gen_printf("INSERT ");
}
else {
gen_printf("UPDATE ");
if (name_list) {
gen_printf("OF ");
gen_name_list(name_list);
gen_printf(" ");
}
}
gen_printf("ON %s", table_name);
if (flags & TRIGGER_FOR_EACH_ROW) {
gen_printf("\n FOR EACH ROW");
}
if (when_expr) {
gen_printf("\n WHEN ");
gen_root_expr(when_expr);
}
gen_printf("\nBEGIN\n");
gen_stmt_list(stmt_list);
gen_printf("END");
gen_version_attrs(trigger_attrs);
}
static void gen_create_table_stmt(ast_node *ast) {
Contract(is_ast_create_table_stmt(ast));
EXTRACT_NOTNULL(create_table_name_flags, ast->left);
EXTRACT_NOTNULL(table_flags_attrs, create_table_name_flags->left);
EXTRACT_OPTION(flags, table_flags_attrs->left);
EXTRACT_ANY(table_attrs, table_flags_attrs->right);
EXTRACT_STRING(name, create_table_name_flags->right);
EXTRACT_NOTNULL(col_key_list, ast->right);
bool_t temp = !!(flags & TABLE_IS_TEMP);
bool_t if_not_exist = !!(flags & TABLE_IF_NOT_EXISTS);
bool_t no_rowid = !!(flags & TABLE_IS_NO_ROWID);
gen_printf("CREATE ");
if (temp) {
gen_printf("TEMP ");
}
gen_printf("TABLE ");
gen_if_not_exists(ast, if_not_exist);
gen_printf("%s(\n", name);
gen_col_key_list(col_key_list);
gen_printf("\n)");
if (no_rowid) {
gen_printf(" WITHOUT ROWID");
}
gen_version_attrs(table_attrs);
}
static void gen_create_virtual_table_stmt(ast_node *ast) {
Contract(is_ast_create_virtual_table_stmt(ast));
EXTRACT_NOTNULL(module_info, ast->left);
EXTRACT_NOTNULL(create_table_stmt, ast->right);
EXTRACT_NOTNULL(create_table_name_flags, create_table_stmt->left);
EXTRACT_NOTNULL(table_flags_attrs, create_table_name_flags->left);
EXTRACT_OPTION(flags, table_flags_attrs->left);
EXTRACT_ANY(table_attrs, table_flags_attrs->right);
EXTRACT_STRING(name, create_table_name_flags->right);
EXTRACT_NOTNULL(col_key_list, create_table_stmt->right);
EXTRACT_STRING(module_name, module_info->left);
EXTRACT_ANY(module_args, module_info->right);
bool_t if_not_exist = !!(flags & TABLE_IF_NOT_EXISTS);
bool_t is_eponymous = !!(flags & VTAB_IS_EPONYMOUS);
gen_printf("CREATE VIRTUAL TABLE ");
gen_if_not_exists(ast, if_not_exist);
gen_eponymous(ast, is_eponymous);
gen_printf("%s USING %s", name, module_name);
if (!for_sqlite()) {
if (is_ast_following(module_args)) {
gen_printf(" (ARGUMENTS FOLLOWING) ");
}
else if (module_args) {
gen_printf(" ");
gen_misc_attr_value(module_args);
gen_printf(" ");
}
else {
gen_printf(" ");
}
// When emitting to SQLite we do not include the column declaration part
// just whatever the args were because SQLite doesn't parse that part of the CQL syntax.
// Note that CQL does not support general args because that's not parseable with this parser
// tech but this is pretty general. The declaration part is present here so that
// CQL knows the type info of the net table we are creating.
// Note also that virtual tables are always on the recreate plan, it isn't an option
// and this will mean that you can't make a foreign key to a virtual table which is probably
// a wise thing.
gen_printf("AS (\n");
gen_col_key_list(col_key_list);
gen_printf("\n)");
// delete attribute is the only option (recreate by default)
if (!is_ast_recreate_attr(table_attrs)) {
Invariant(is_ast_delete_attr(table_attrs));
gen_delete_attr(table_attrs);
}
}
else {
if (is_ast_following(module_args)) {
gen_printf(" (\n");
gen_col_key_list(col_key_list);
gen_printf(")");
} else if (module_args) {
gen_printf(" ");
gen_misc_attr_value(module_args);
}
}
}
static void gen_drop_view_stmt(ast_node *ast) {
Contract(is_ast_drop_view_stmt(ast));
EXTRACT_ANY(if_exists, ast->left);
EXTRACT_STRING(name, ast->right);
gen_printf("DROP VIEW ");
if (if_exists) {
gen_printf("IF EXISTS ");
}
gen_printf("%s", name);
}
static void gen_drop_table_stmt(ast_node *ast) {
Contract(is_ast_drop_table_stmt(ast));
EXTRACT_ANY(if_exists, ast->left);
EXTRACT_STRING(name, ast->right);
gen_printf("DROP TABLE ");
if (if_exists) {
gen_printf("IF EXISTS ");
}
gen_printf("%s", name);
}
static void gen_drop_index_stmt(ast_node *ast) {
Contract(is_ast_drop_index_stmt(ast));
EXTRACT_ANY(if_exists, ast->left);
EXTRACT_STRING(name, ast->right);
gen_printf("DROP INDEX ");
if (if_exists) {
gen_printf("IF EXISTS ");
}
gen_printf("%s", name);
}
static void gen_drop_trigger_stmt(ast_node *ast) {
Contract(is_ast_drop_trigger_stmt(ast));
EXTRACT_ANY(if_exists, ast->left);
EXTRACT_STRING(name, ast->right);
gen_printf("DROP TRIGGER ");
if (if_exists) {
gen_printf("IF EXISTS ");
}
gen_printf("%s", name);
}
static void gen_alter_table_add_column_stmt(ast_node *ast) {
Contract(is_ast_alter_table_add_column_stmt(ast));
EXTRACT_STRING(name, ast->left);
EXTRACT(col_def, ast->right);
gen_printf("ALTER TABLE %s ADD COLUMN ", name);
gen_col_def(col_def);
}
static void gen_cond_action(ast_node *ast) {
Contract(is_ast_cond_action(ast));
EXTRACT(stmt_list, ast->right);
gen_root_expr(ast->left);
gen_printf(" THEN\n");
gen_stmt_list(stmt_list);
}
static void gen_elseif_list(ast_node *ast) {
Contract(is_ast_elseif(ast));
while (ast) {
Contract(is_ast_elseif(ast));
EXTRACT(cond_action, ast->left);
gen_printf("ELSE IF ");
gen_cond_action(cond_action);
ast = ast->right;
}
}
static void gen_if_stmt(ast_node *ast) {
Contract(is_ast_if_stmt(ast));
EXTRACT_NOTNULL(cond_action, ast->left);
EXTRACT_NOTNULL(if_alt, ast->right);
EXTRACT(elseif, if_alt->left);
EXTRACT_NAMED(elsenode, else, if_alt->right);
gen_printf("IF ");
gen_cond_action(cond_action);
if (elseif) {
gen_elseif_list(elseif);
}
if (elsenode) {
gen_printf("ELSE\n");
EXTRACT(stmt_list, elsenode->left);
gen_stmt_list(stmt_list);
}
gen_printf("END IF");
}
static void gen_guard_stmt(ast_node *ast) {
Contract(is_ast_guard_stmt(ast));
EXTRACT_ANY_NOTNULL(expr, ast->left);
EXTRACT_ANY_NOTNULL(stmt, ast->right);
gen_printf("IF ");
gen_expr(expr, EXPR_PRI_ROOT);
gen_printf(" ");
gen_one_stmt(stmt);
}
static void gen_delete_stmt(ast_node *ast) {
Contract(is_ast_delete_stmt(ast));
EXTRACT_STRING(name, ast->left);
EXTRACT(opt_where, ast->right);
gen_printf("DELETE FROM %s", name);
if (opt_where) {
gen_printf(" WHERE ");
gen_root_expr(opt_where->left);
}
}
static void gen_with_delete_stmt(ast_node *ast) {
Contract(is_ast_with_delete_stmt(ast));
EXTRACT_ANY_NOTNULL(with_prefix, ast->left)
EXTRACT_NOTNULL(delete_stmt, ast->right);
gen_with_prefix(with_prefix);
gen_delete_stmt(delete_stmt);
}
static void gen_update_entry(ast_node *ast) {
Contract(is_ast_update_entry(ast));
EXTRACT_ANY_NOTNULL(expr, ast->right)
EXTRACT_STRING(name, ast->left);
gen_printf("%s = ", name);
gen_root_expr(expr);
}
static void gen_update_list(ast_node *ast) {
Contract(is_ast_update_list(ast));
for (ast_node *item = ast; item; item = item->right) {
Contract(is_ast_update_list(item));
EXTRACT_NOTNULL(update_entry, item->left);
gen_update_entry(update_entry);
if (item->right) {
gen_printf(",\n");
}
}
}
static void gen_from_shape(ast_node *ast) {
Contract(is_ast_from_shape(ast));
EXTRACT_STRING(shape_name, ast->right);
EXTRACT_ANY(column_spec, ast->left);
gen_printf("FROM %s", shape_name);
gen_column_spec(column_spec);
}
static void gen_update_cursor_stmt(ast_node *ast) {
Contract(is_ast_update_cursor_stmt(ast));
EXTRACT_ANY(cursor, ast->left);
EXTRACT_STRING(name, cursor);
EXTRACT_ANY_NOTNULL(columns_values, ast->right);
gen_printf("UPDATE CURSOR %s", name);
if (is_ast_expr_names(columns_values)) {
gen_printf(" USING ");
gen_expr_names(columns_values);
}
else {
EXTRACT_ANY(column_spec, columns_values->left);
EXTRACT_ANY(insert_list, columns_values->right);
gen_column_spec(column_spec);
gen_printf(" ");
if (is_ast_from_shape(insert_list)) {
gen_from_shape(insert_list);
}
else {
gen_printf("FROM VALUES(");
gen_insert_list(insert_list);
gen_printf(")");
}
}
}
static void gen_update_stmt(ast_node *ast) {
Contract(is_ast_update_stmt(ast));
EXTRACT_NOTNULL(update_set, ast->right);
EXTRACT_NOTNULL(update_list, update_set->left);
EXTRACT_NOTNULL(update_where, update_set->right);
EXTRACT(opt_where, update_where->left);
EXTRACT_NOTNULL(update_orderby, update_where->right);
EXTRACT(opt_orderby, update_orderby->left);
EXTRACT(opt_limit, update_orderby->right);
gen_printf("UPDATE");
if (ast->left) {
EXTRACT_STRING(name, ast->left);
gen_printf(" %s", name);
}
gen_printf("\nSET ");
gen_update_list(update_list);
if (opt_where) {
gen_opt_where(opt_where);
}
if (opt_orderby) {
gen_printf("\n");
gen_opt_orderby(opt_orderby);
}
if (opt_limit) {
gen_opt_limit(opt_limit);
}
}
static void gen_with_update_stmt(ast_node *ast) {
Contract(is_ast_with_update_stmt(ast));
EXTRACT_ANY_NOTNULL(with_prefix, ast->left)
EXTRACT_NOTNULL(update_stmt, ast->right);
gen_with_prefix(with_prefix);
gen_update_stmt(update_stmt);
}
static void gen_insert_list(ast_node *_Nullable ast) {
Contract(!ast || is_ast_insert_list(ast));
while (ast) {
Contract(is_ast_insert_list(ast));
if (is_ast_from_shape(ast->left)) {
gen_shape_arg(ast->left);
}
else {
gen_root_expr(ast->left);
}
if (ast->right) {
gen_printf(", ");
}
ast = ast->right;
}
}
cql_noexport void gen_insert_type(ast_node *ast) {
if (is_ast_insert_or_ignore(ast)) {
gen_printf("INSERT OR IGNORE");
}
else if (is_ast_insert_or_replace(ast)) {
gen_printf("INSERT OR REPLACE");
}
else if (is_ast_insert_replace(ast)) {
gen_printf("REPLACE");
}
else if (is_ast_insert_or_abort(ast)) {
gen_printf("INSERT OR ABORT");
}
else if (is_ast_insert_or_fail(ast)) {
gen_printf("INSERT OR FAIL");
}
else if (is_ast_insert_or_rollback(ast)) {
gen_printf("INSERT OR ROLLBACK");
}
else {
Contract(is_ast_insert_normal(ast));
gen_printf("INSERT");
}
}
static void gen_insert_dummy_spec(ast_node *ast) {
Contract(is_ast_insert_dummy_spec(ast));
EXTRACT_ANY_NOTNULL(seed_expr, ast->left);
EXTRACT_OPTION(flags, ast->right);
if (suppress_attributes()) {
return;
}
gen_printf(" @DUMMY_SEED(");
gen_root_expr(seed_expr);
gen_printf(")");
if (flags & INSERT_DUMMY_DEFAULTS) {
gen_printf(" @DUMMY_DEFAULTS");
}
if (flags & INSERT_DUMMY_NULLABLES) {
gen_printf(" @DUMMY_NULLABLES");
}
}
static void gen_shape_def(ast_node *ast) {
Contract(is_ast_like(ast));
EXTRACT_STRING(name, ast->left);
EXTRACT_ANY(from_args, ast->right);
gen_printf("LIKE %s", name);
if (from_args) {
gen_printf(" ARGUMENTS");
}
}
static void gen_column_spec(ast_node *ast) {
// allow null column_spec here so we don't have to test it everywhere
if (ast) {
gen_printf("(");
if (is_ast_like(ast->left)) {
gen_shape_def(ast->left);
}
else {
EXTRACT(name_list, ast->left);
if (name_list) {
gen_name_list(name_list);
}
}
gen_printf(")");
}
}
static void gen_insert_stmt(ast_node *ast) {
Contract(is_ast_insert_stmt(ast));
EXTRACT_ANY_NOTNULL(insert_type, ast->left);
EXTRACT_NOTNULL(name_columns_values, ast->right);
EXTRACT_STRING(name, name_columns_values->left);
EXTRACT_ANY_NOTNULL(columns_values, name_columns_values->right);
EXTRACT(insert_dummy_spec, insert_type->left);
gen_insert_type(insert_type);
gen_printf(" INTO %s", name);
if (is_ast_expr_names(columns_values)) {
gen_printf(" USING ");
gen_expr_names(columns_values);
}
else if (is_select_stmt(columns_values)) {
gen_printf(" USING ");
gen_select_stmt(columns_values);
}
else if (is_ast_columns_values(columns_values)) {
EXTRACT(column_spec, columns_values->left);
EXTRACT_ANY(insert_list, columns_values->right);
gen_column_spec(column_spec);
gen_printf(" ");
if (is_select_stmt(insert_list)) {
gen_select_stmt(insert_list);
}
else if (is_ast_from_shape(insert_list)) {
gen_from_shape(insert_list);
}
else {
gen_printf("VALUES(");
gen_insert_list(insert_list);
gen_printf(")");
}
if (insert_dummy_spec) {
gen_insert_dummy_spec(insert_dummy_spec);
}
}
else {
// INSERT [conflict resolution] INTO name DEFAULT VALUES
Contract(is_ast_default_columns_values(columns_values));
gen_printf(" DEFAULT VALUES");
}
}
static void gen_with_insert_stmt(ast_node *ast) {
Contract(is_ast_with_insert_stmt(ast));
EXTRACT_ANY_NOTNULL(with_prefix, ast->left)
EXTRACT_NOTNULL(insert_stmt, ast->right);
gen_with_prefix(with_prefix);
gen_insert_stmt(insert_stmt);
}
static void gen_expr_names(ast_node *ast) {
Contract(is_ast_expr_names(ast));
for (ast_node *list = ast; list; list = list->right) {
EXTRACT(expr_name, list->left);
EXTRACT_ANY(expr, expr_name->left);
EXTRACT_NOTNULL(opt_as_alias, expr_name->right);
gen_expr(expr, EXPR_PRI_ROOT);
gen_as_alias(opt_as_alias);
if (list->right) {
gen_printf(", ");
}
}
}
static void gen_fetch_cursor_from_blob_stmt(ast_node *ast) {
Contract(is_ast_fetch_cursor_from_blob_stmt(ast));
EXTRACT_ANY_NOTNULL(cursor, ast->left);
EXTRACT_ANY_NOTNULL(blob, ast->right);
gen_printf("FETCH ");
gen_expr(cursor, EXPR_PRI_ROOT);
gen_printf(" FROM BLOB ");
gen_expr(blob, EXPR_PRI_ROOT);
}
static void gen_set_blob_from_cursor_stmt(ast_node *ast) {
Contract(is_ast_set_blob_from_cursor_stmt(ast));
EXTRACT_ANY_NOTNULL(blob, ast->left);
EXTRACT_ANY_NOTNULL(cursor, ast->right);
gen_printf("SET ");
gen_expr(blob, EXPR_PRI_ROOT);
gen_printf(" FROM CURSOR ");
gen_expr(cursor, EXPR_PRI_ROOT);
}
static void gen_fetch_values_stmt(ast_node *ast) {
Contract(is_ast_fetch_values_stmt(ast));
EXTRACT(insert_dummy_spec, ast->left);
EXTRACT_NOTNULL(name_columns_values, ast->right);
EXTRACT_STRING(name, name_columns_values->left);
EXTRACT_ANY_NOTNULL(columns_values, name_columns_values->right);
gen_printf("FETCH %s", name);
if (is_ast_expr_names(columns_values)) {
gen_printf(" USING ");
gen_expr_names(columns_values);
} else {
EXTRACT(column_spec, columns_values->left);
gen_column_spec(column_spec);
gen_printf(" ");
if (is_ast_from_shape(columns_values->right)) {
gen_from_shape(columns_values->right);
}
else {
EXTRACT(insert_list, columns_values->right);
gen_printf("FROM VALUES(", name);
gen_insert_list(insert_list);
gen_printf(")");
}
}
if (insert_dummy_spec) {
gen_insert_dummy_spec(insert_dummy_spec);
}
}
static void gen_assign(ast_node *ast) {
Contract(is_ast_assign(ast));
EXTRACT_STRING(name, ast->left);
EXTRACT_ANY_NOTNULL(expr, ast->right);
gen_printf("SET %s := ", name);
gen_root_expr(expr);
}
static void gen_let_stmt(ast_node *ast) {
Contract(is_ast_let_stmt(ast));
EXTRACT_STRING(name, ast->left);
EXTRACT_ANY_NOTNULL(expr, ast->right);
gen_printf("LET %s := ", name);
gen_root_expr(expr);
}
static void gen_opt_inout(ast_node *ast) {
if (is_ast_in(ast)) {
gen_printf("IN ");
}
else if (is_ast_out(ast)) {
gen_printf("OUT ");
}
else if (is_ast_inout(ast)) {
gen_printf("INOUT ");
}
else {
Contract(!ast);
}
}
static void gen_normal_param(ast_node *ast) {
Contract(is_ast_param(ast));
EXTRACT_ANY(opt_inout, ast->left);
EXTRACT_NOTNULL(param_detail, ast->right);
EXTRACT_STRING(name, param_detail->left);
EXTRACT_ANY_NOTNULL(data_type, param_detail->right);
gen_opt_inout(opt_inout);
gen_printf("%s ", name);
gen_data_type(data_type);
}
static void gen_like_param(ast_node *ast) {
Contract(is_ast_param(ast));
EXTRACT_NOTNULL(param_detail, ast->right);
EXTRACT_NOTNULL(like, param_detail->right);
if (param_detail->left) {
EXTRACT_STRING(name, param_detail->left);
gen_printf("%s ", name);
}
gen_shape_def(like);
}
static void gen_param(ast_node *ast) {
Contract(is_ast_param(ast));
EXTRACT_NOTNULL(param_detail, ast->right);
if (is_ast_like(param_detail->right)) {
gen_like_param(ast);
}
else {
gen_normal_param(ast);
}
}
cql_noexport void gen_params(ast_node *ast) {
Contract(is_ast_params(ast));
for (ast_node *cur = ast; cur; cur = cur->right) {
Contract(is_ast_params(cur));
EXTRACT_NOTNULL(param, cur->left);
gen_param(param);
if (cur->right) {
gen_printf(", ");
}
}
}
static void gen_create_proc_stmt(ast_node *ast) {
Contract(is_ast_create_proc_stmt(ast));
EXTRACT_STRING(name, ast->left);
EXTRACT_NOTNULL(proc_params_stmts, ast->right);
EXTRACT(params, proc_params_stmts->left);
EXTRACT(stmt_list, proc_params_stmts->right);
gen_printf("CREATE PROC %s (", name);
if (params) {
gen_params(params);
}
gen_printf(")\nBEGIN\n");
gen_stmt_list(stmt_list);
gen_printf("END");
}
cql_noexport void gen_declare_proc_from_create_proc(ast_node *ast) {
Contract(is_ast_create_proc_stmt(ast));
EXTRACT_STRING(name, ast->left);
EXTRACT_NOTNULL(proc_params_stmts, ast->right);
EXTRACT(params, proc_params_stmts->left);
gen_printf("DECLARE PROC %s (", name);
if (params) {
gen_params(params);
}
gen_printf(")");
#if defined(CQL_AMALGAM_LEAN) && !defined(CQL_AMALGAM_SEM)
// if no SEM then we can't do the full declaration, do the best we can with just AST
#else
if (ast->sem) {
if (has_out_stmt_result(ast)) {
gen_printf(" OUT");
}
if (has_out_union_stmt_result(ast)) {
gen_printf(" OUT UNION");
}
if (is_struct(ast->sem->sem_type)) {
sem_struct *sptr = ast->sem->sptr;
gen_printf(" (");
for (int32_t i = 0; i < sptr->count; i++) {
gen_printf("%s ", sptr->names[i]);
sem_t sem_type = sptr->semtypes[i];
gen_printf("%s", coretype_string(sem_type));
if (is_not_nullable(sem_type)) {
gen_printf(" NOT NULL");
}
if (sensitive_flag(sem_type) && !for_sqlite()) {
gen_printf(" @SENSITIVE");
}
if (i + 1 < sptr->count) {
gen_printf(", ");
}
}
gen_printf(")");
if ((has_out_stmt_result(ast) || has_out_union_stmt_result(ast)) && is_dml_proc(ast->sem->sem_type)) {
// out [union] can be DML or not, so we have to specify
gen_printf(" USING TRANSACTION");
}
}
else if (is_dml_proc(ast->sem->sem_type)) {
gen_printf(" USING TRANSACTION");
}
}
#endif
}
static void gen_typed_name(ast_node *ast) {
EXTRACT(typed_name, ast);
EXTRACT_ANY(name, typed_name->left);
EXTRACT_ANY_NOTNULL(type, typed_name->right);
if (name) {
EXTRACT_STRING(formal, name);
gen_printf("%s ", formal);
}
if (is_ast_like(type)) {
gen_shape_def(type);
}
else {
gen_data_type(type);
}
}
static void gen_typed_names(ast_node *ast) {
Contract(is_ast_typed_names(ast));
while (ast) {
Contract(is_ast_typed_names(ast));
gen_typed_name(ast->left);
if (ast->right) {
gen_printf(", ");
}
ast = ast->right;
}
}
static void gen_declare_proc_no_check_stmt(ast_node *ast) {
Contract(is_ast_declare_proc_no_check_stmt(ast));
EXTRACT_ANY_NOTNULL(proc_name, ast->left);
EXTRACT_STRING(name, proc_name);
gen_printf("DECLARE PROC %s NO CHECK", name);
}
static void gen_declare_proc_stmt(ast_node *ast) {
Contract(is_ast_declare_proc_stmt(ast));
EXTRACT_NOTNULL(proc_name_type, ast->left);
EXTRACT_STRING(name, proc_name_type->left);
EXTRACT_OPTION(type, proc_name_type->right);
EXTRACT_NOTNULL(proc_params_stmts, ast->right);
EXTRACT(params, proc_params_stmts->left);
EXTRACT(typed_names, proc_params_stmts->right);
gen_printf("DECLARE PROC %s (", name);
if (params) {
gen_params(params);
}
gen_printf(")");
if (type & PROC_FLAG_USES_OUT) {
gen_printf(" OUT");
}
if (type & PROC_FLAG_USES_OUT_UNION) {
gen_printf(" OUT UNION");
}
if (typed_names) {
Contract(type & PROC_FLAG_STRUCT_TYPE);
gen_printf(" (");
gen_typed_names(typed_names);
gen_printf(")");
}
// we don't emit USING TRANSACTION unless it's needed
// if it doesnt use DML it's not needed
if (!(type & PROC_FLAG_USES_DML)) {
return;
}
// out can be either, so emit it if needed
if (type & (PROC_FLAG_USES_OUT | PROC_FLAG_USES_OUT_UNION)) {
gen_printf(" USING TRANSACTION");
return;
}
// if the proc returns a struct not via out then it uses SELECT and so it's implictly DML
if (type & PROC_FLAG_STRUCT_TYPE) {
return;
}
// it's not an OUT and it doesn't have a result but it does use DML
// the only flag combo left is a basic dml proc.
Contract(type == PROC_FLAG_USES_DML);
gen_printf(" USING TRANSACTION");
}
cql_noexport void gen_declare_proc_from_create_or_decl(ast_node *ast) {
Contract(is_ast_create_proc_stmt(ast) || is_ast_declare_proc_stmt(ast));
if (is_ast_create_proc_stmt(ast)) {
gen_declare_proc_from_create_proc(ast);
}
else {
gen_declare_proc_stmt(ast);
}
}
static void gen_declare_select_func_stmt(ast_node *ast) {
Contract(is_ast_declare_select_func_stmt(ast));
EXTRACT_STRING(name, ast->left);
EXTRACT_NOTNULL(func_params_return, ast->right);
EXTRACT(params, func_params_return->left);
EXTRACT_ANY_NOTNULL(ret_data_type, func_params_return->right);
gen_printf("DECLARE SELECT FUNC %s (", name);
if (params) {
gen_params(params);
}
gen_printf(") ");
if (is_ast_typed_names(ret_data_type)) {
// table valued function
gen_printf("(");
gen_typed_names(ret_data_type);
gen_printf(")");
}
else {
// standard function
gen_data_type(ret_data_type);
}
}
static void gen_declare_func_stmt(ast_node *ast) {
Contract(is_ast_declare_func_stmt(ast));
EXTRACT_STRING(name, ast->left);
EXTRACT_NOTNULL(func_params_return, ast->right);
EXTRACT(params, func_params_return->left);
EXTRACT_ANY_NOTNULL(ret_data_type, func_params_return->right);
gen_printf("DECLARE FUNC %s (", name);
if (params) {
gen_params(params);
}
gen_printf(") ");
gen_data_type(ret_data_type);
}
static void gen_declare_vars_type(ast_node *ast) {
Contract(is_ast_declare_vars_type(ast));
EXTRACT_NOTNULL(name_list, ast->left);
EXTRACT_ANY_NOTNULL(data_type, ast->right);
gen_printf("DECLARE ");
gen_name_list(name_list);
gen_printf(" ");
gen_data_type(data_type);
}
static void gen_declare_cursor(ast_node *ast) {
Contract(is_ast_declare_cursor(ast));
EXTRACT_STRING(name, ast->left);
EXTRACT_ANY_NOTNULL(source, ast->right);
gen_printf("DECLARE %s CURSOR FOR ", name);
if (is_ast_str(source)) {
// The unboxing case gives a name rather than a statement
EXTRACT_STRING(var_name, ast->right);
gen_printf("%s", var_name);
}
else {
// The two statement cases are unified
gen_one_stmt(source);
}
}
static void gen_declare_cursor_like_name(ast_node *ast) {
Contract(is_ast_declare_cursor_like_name(ast));
EXTRACT_STRING(new_cursor_name, ast->left);
EXTRACT_NOTNULL(like, ast->right);
gen_printf("DECLARE %s CURSOR ", new_cursor_name);
gen_shape_def(like);
}
static void gen_declare_cursor_like_select(ast_node *ast) {
Contract(is_ast_declare_cursor_like_select(ast));
EXTRACT_STRING(name, ast->left);
EXTRACT_ANY_NOTNULL(stmt, ast->right);
gen_printf("DECLARE %s CURSOR LIKE ", name);
gen_one_stmt(stmt);
}
static void gen_declare_named_type(ast_node *ast) {
Contract(is_ast_declare_named_type(ast));
EXTRACT_STRING(name, ast->left);
EXTRACT_ANY_NOTNULL(data_type, ast->right);
gen_printf("DECLARE %s TYPE ", name);
gen_data_type(data_type);
}
static void gen_declare_value_cursor(ast_node *ast) {
Contract(is_ast_declare_value_cursor(ast));
EXTRACT_STRING(name, ast->left);
EXTRACT_ANY_NOTNULL(stmt, ast->right);
gen_printf("DECLARE %s CURSOR FETCH FROM ", name);
gen_one_stmt(stmt);
}
static void gen_declare_enum_stmt(ast_node *ast) {
Contract(is_ast_declare_enum_stmt(ast));
EXTRACT_NOTNULL(typed_name, ast->left);
EXTRACT_NOTNULL(enum_values, ast->right);
gen_printf("DECLARE ENUM ");
gen_typed_name(typed_name);
gen_printf(" (");
while (enum_values) {
EXTRACT_NOTNULL(enum_value, enum_values->left);
EXTRACT_STRING(enum_name, enum_value->left);
EXTRACT_ANY(expr, enum_value->right);
gen_printf("\n %s", enum_name);
if (expr) {
gen_printf(" = ");
gen_root_expr(expr);
}
if (enum_values->right) {
gen_printf(",");
}
enum_values = enum_values->right;
}
gen_printf("\n)");
}
static void gen_declare_group_stmt(ast_node *ast) {
Contract(is_ast_declare_group_stmt(ast));
EXTRACT_STRING(name, ast->left);
EXTRACT_NOTNULL(stmt_list, ast->right);
gen_printf("DECLARE GROUP %s\nBEGIN\n", name);
while (stmt_list) {
EXTRACT_ANY_NOTNULL(stmt, stmt_list->left);
gen_printf(" ");
gen_one_stmt(stmt);
gen_printf(";\n");
stmt_list = stmt_list->right;
}
gen_printf("END");
}
static void gen_declare_const_stmt(ast_node *ast) {
Contract(is_ast_declare_const_stmt(ast));
EXTRACT_STRING(name, ast->left);
EXTRACT_NOTNULL(const_values, ast->right);
gen_printf("DECLARE CONST GROUP %s (", name);
while (const_values) {
EXTRACT_NOTNULL(const_value, const_values->left);
EXTRACT_STRING(const_name, const_value->left);
EXTRACT_ANY(expr, const_value->right);
gen_printf("\n %s", const_name);
if (expr) {
gen_printf(" = ");
gen_root_expr(expr);
}
if (const_values->right) {
gen_printf(",");
}
const_values = const_values->right;
}
gen_printf("\n)");
}
static void gen_set_from_cursor(ast_node *ast) {
Contract(is_ast_set_from_cursor(ast));
EXTRACT_STRING(var_name, ast->left);
EXTRACT_STRING(cursor_name, ast->right);
gen_printf("SET %s FROM CURSOR %s", var_name, cursor_name);
}
static void gen_fetch_stmt(ast_node *ast) {
Contract(is_ast_fetch_stmt(ast));
EXTRACT_STRING(name, ast->left);
EXTRACT(name_list, ast->right);
gen_printf("FETCH %s", name);
if (name_list) {
gen_printf(" INTO ", name);
gen_name_list(name_list);
}
}
static void gen_switch_cases(ast_node *ast) {
Contract(is_ast_switch_case(ast));
while (ast) {
EXTRACT_NOTNULL(connector, ast->left);
if (connector->left) {
EXTRACT_NOTNULL(expr_list, connector->left);
EXTRACT(stmt_list, connector->right);
gen_printf(" WHEN ");
gen_expr_list(expr_list);
if (stmt_list) {
gen_printf(" THEN\n");
BEGIN_INDENT(statement, 2);
gen_stmt_list(stmt_list);
END_INDENT(statement);
}
else {
gen_printf(" THEN NOTHING\n");
}
}
else {
EXTRACT_NOTNULL(stmt_list, connector->right);
gen_printf(" ELSE\n");
BEGIN_INDENT(statement, 2);
gen_stmt_list(stmt_list);
END_INDENT(statement);
}
ast = ast->right;
}
gen_printf("END");
}
static void gen_switch_stmt(ast_node *ast) {
Contract(is_ast_switch_stmt(ast));
EXTRACT_OPTION(all_values, ast->left);
EXTRACT_NOTNULL(switch_body, ast->right);
EXTRACT_ANY_NOTNULL(expr, switch_body->left);
EXTRACT_NOTNULL(switch_case, switch_body->right);
// SWITCH [expr] [switch_body] END
// SWITCH [expr] ALL VALUES [switch_body] END
gen_printf("SWITCH ");
gen_root_expr(expr);
if (all_values) {
gen_printf(" ALL VALUES");
}
gen_printf("\n");
gen_switch_cases(switch_case);
}
static void gen_while_stmt(ast_node *ast) {
Contract(is_ast_while_stmt(ast));
EXTRACT_ANY_NOTNULL(expr, ast->left);
EXTRACT(stmt_list, ast->right);
// WHILE [expr] BEGIN [stmt_list] END
gen_printf("WHILE ");
gen_root_expr(expr);
gen_printf("\nBEGIN\n");
gen_stmt_list(stmt_list);
gen_printf("END");
}
static void gen_loop_stmt(ast_node *ast) {
Contract(is_ast_loop_stmt(ast));
EXTRACT_NOTNULL(fetch_stmt, ast->left);
EXTRACT(stmt_list, ast->right);
// LOOP [fetch_stmt] BEGIN [stmt_list] END
gen_printf("LOOP ");
gen_fetch_stmt(fetch_stmt);
gen_printf("\nBEGIN\n");
gen_stmt_list(stmt_list);
gen_printf("END");
}
static void gen_call_stmt(ast_node *ast) {
Contract(is_ast_call_stmt(ast));
EXTRACT_STRING(name, ast->left);
EXTRACT(expr_list, ast->right);
gen_printf("CALL %s(", name);
if (expr_list) {
gen_call_expr_list(expr_list);
}
gen_printf(")", name);
}
static void gen_declare_out_call_stmt(ast_node *ast) {
EXTRACT_NOTNULL(call_stmt, ast->left);
gen_printf("DECLARE OUT ");
gen_call_stmt(call_stmt);
}
static void gen_fetch_call_stmt(ast_node *ast) {
Contract(is_ast_fetch_call_stmt(ast));
Contract(is_ast_call_stmt(ast->right));
EXTRACT_STRING(cursor_name, ast->left);
EXTRACT_ANY_NOTNULL(call_stmt, ast->right);
gen_printf("FETCH %s FROM ", cursor_name);
gen_call_stmt(call_stmt);
}
static void gen_continue_stmt(ast_node *ast) {
Contract(is_ast_continue_stmt(ast));
gen_printf("CONTINUE");
}
static void gen_leave_stmt(ast_node *ast) {
Contract(is_ast_leave_stmt(ast));
gen_printf("LEAVE");
}
static void gen_return_stmt(ast_node *ast) {
Contract(is_ast_return_stmt(ast));
gen_printf("RETURN");
}
static void gen_rollback_return_stmt(ast_node *ast) {
Contract(is_ast_rollback_return_stmt(ast));
gen_printf("ROLLBACK RETURN");
}
static void gen_commit_return_stmt(ast_node *ast) {
Contract(is_ast_commit_return_stmt(ast));
gen_printf("COMMIT RETURN");
}
static void gen_proc_savepoint_stmt(ast_node *ast) {
Contract(is_ast_proc_savepoint_stmt(ast));
EXTRACT(stmt_list, ast->left);
gen_printf("PROC SAVEPOINT");
gen_printf("\nBEGIN\n");
gen_stmt_list(stmt_list);
gen_printf("END");
}
static void gen_throw_stmt(ast_node *ast) {
Contract(is_ast_throw_stmt(ast));
gen_printf("THROW");
}
static void gen_begin_trans_stmt(ast_node *ast) {
Contract(is_ast_begin_trans_stmt(ast));
EXTRACT_OPTION(mode, ast->left);
gen_printf("BEGIN");
if (mode == TRANS_IMMEDIATE) {
gen_printf(" IMMEDIATE");
}
else if (mode == TRANS_EXCLUSIVE) {
gen_printf(" EXCLUSIVE");
}
else {
// this is the default, and only remaining case, no additional output needed
Contract(mode == TRANS_DEFERRED);
}
}
static void gen_commit_trans_stmt(ast_node *ast) {
Contract(is_ast_commit_trans_stmt(ast));
gen_printf("COMMIT");
}
static void gen_rollback_trans_stmt(ast_node *ast) {
Contract(is_ast_rollback_trans_stmt(ast));
gen_printf("ROLLBACK");
if (ast->left) {
EXTRACT_STRING(name, ast->left);
gen_printf(" TO %s", name);
}
}
static void gen_savepoint_stmt(ast_node *ast) {
Contract(is_ast_savepoint_stmt(ast));
EXTRACT_STRING(name, ast->left);
gen_printf("SAVEPOINT %s", name);
}
static void gen_release_savepoint_stmt(ast_node *ast) {
Contract(is_ast_release_savepoint_stmt(ast));
EXTRACT_STRING(name, ast->left);
gen_printf("RELEASE %s", name);
}
static void gen_trycatch_stmt(ast_node *ast) {
Contract(is_ast_trycatch_stmt(ast));
EXTRACT_NAMED(try_list, stmt_list, ast->left);
EXTRACT_NAMED(catch_list, stmt_list, ast->right);
gen_printf("BEGIN TRY\n");
gen_stmt_list(try_list);
gen_printf("END TRY;\n");
gen_printf("BEGIN CATCH\n");
gen_stmt_list(catch_list);
gen_printf("END CATCH");
}
static void gen_close_stmt(ast_node *ast) {
Contract(is_ast_close_stmt(ast));
EXTRACT_STRING(name, ast->left);
gen_printf("CLOSE %s", name);
}
static void gen_out_stmt(ast_node *ast) {
Contract(is_ast_out_stmt(ast));
EXTRACT_STRING(name, ast->left);
gen_printf("OUT %s", name);
}
static void gen_out_union_stmt(ast_node *ast) {
Contract(is_ast_out_union_stmt(ast));
EXTRACT_STRING(name, ast->left);
gen_printf("OUT UNION %s", name);
}
static void gen_echo_stmt(ast_node *ast) {
Contract(is_ast_echo_stmt(ast));
EXTRACT_STRING(rt_name, ast->left);
gen_printf("@ECHO %s, ", rt_name);
gen_root_expr(ast->right); // emit the quoted literal
}
static void gen_schema_upgrade_script_stmt(ast_node *ast) {
Contract(is_ast_schema_upgrade_script_stmt(ast));
gen_printf("@SCHEMA_UPGRADE_SCRIPT");
}
static void gen_schema_upgrade_version_stmt(ast_node *ast) {
Contract(is_ast_schema_upgrade_version_stmt(ast));
EXTRACT_OPTION(vers, ast->left);
gen_printf("@SCHEMA_UPGRADE (%d)", vers);
}
static void gen_previous_schema_stmt(ast_node *ast) {
Contract(is_ast_previous_schema_stmt(ast));
gen_printf("@PREVIOUS_SCHEMA");
}
static void gen_enforcement_options(ast_node *ast) {
EXTRACT_OPTION(option, ast);
switch (option) {
case ENFORCE_CAST:
gen_printf("CAST");
break;
case ENFORCE_STRICT_JOIN:
gen_printf("JOIN");
break;
case ENFORCE_FK_ON_UPDATE:
gen_printf("FOREIGN KEY ON UPDATE");
break;
case ENFORCE_UPSERT_STMT:
gen_printf("UPSERT STATEMENT");
break;
case ENFORCE_WINDOW_FUNC:
gen_printf("WINDOW FUNCTION");
break;
case ENFORCE_WITHOUT_ROWID:
gen_printf("WITHOUT ROWID");
break;
case ENFORCE_TRANSACTION:
gen_printf("TRANSACTION");
break;
case ENFORCE_SELECT_IF_NOTHING:
gen_printf("SELECT IF NOTHING");
break;
case ENFORCE_INSERT_SELECT:
gen_printf("INSERT SELECT");
break;
case ENFORCE_TABLE_FUNCTION:
gen_printf("TABLE FUNCTION");
break;
case ENFORCE_ENCODE_CONTEXT_COLUMN:
gen_printf("ENCODE CONTEXT COLUMN");
break;
case ENFORCE_ENCODE_CONTEXT_TYPE_INTEGER:
gen_printf("ENCODE CONTEXT TYPE INTEGER");
break;
case ENFORCE_ENCODE_CONTEXT_TYPE_LONG_INTEGER:
gen_printf("ENCODE CONTEXT TYPE LONG_INTEGER");
break;
case ENFORCE_ENCODE_CONTEXT_TYPE_REAL:
gen_printf("ENCODE CONTEXT TYPE REAL");
break;
case ENFORCE_ENCODE_CONTEXT_TYPE_BOOL:
gen_printf("ENCODE CONTEXT TYPE BOOL");
break;
case ENFORCE_ENCODE_CONTEXT_TYPE_TEXT:
gen_printf("ENCODE CONTEXT TYPE TEXT");
break;
case ENFORCE_ENCODE_CONTEXT_TYPE_BLOB:
gen_printf("ENCODE CONTEXT TYPE BLOB");
break;
case ENFORCE_IS_TRUE:
gen_printf("IS TRUE");
break;
case ENFORCE_SIGN_FUNCTION:
gen_printf("SIGN FUNCTION");
break;
case ENFORCE_CURSOR_HAS_ROW:
gen_printf("CURSOR HAS ROW");
break;
default:
// this is all that's left
Contract(option == ENFORCE_FK_ON_DELETE);
gen_printf("FOREIGN KEY ON DELETE");
break;
}
}
static void gen_enforce_strict_stmt(ast_node *ast) {
Contract(is_ast_enforce_strict_stmt(ast));
gen_printf("@ENFORCE_STRICT ");
gen_enforcement_options(ast->left);
}
static void gen_enforce_normal_stmt(ast_node *ast) {
Contract(is_ast_enforce_normal_stmt(ast));
gen_printf("@ENFORCE_NORMAL ");
gen_enforcement_options(ast->left);
}
static void gen_enforce_reset_stmt(ast_node *ast) {
Contract(is_ast_enforce_reset_stmt(ast));
gen_printf("@ENFORCE_RESET");
}
static void gen_enforce_push_stmt(ast_node *ast) {
Contract(is_ast_enforce_push_stmt(ast));
gen_printf("@ENFORCE_PUSH");
}
static void gen_enforce_pop_stmt(ast_node *ast) {
Contract(is_ast_enforce_pop_stmt(ast));
gen_printf("@ENFORCE_POP");
}
static void gen_region_spec(ast_node *ast) {
Contract(is_ast_region_spec(ast));
EXTRACT_OPTION(type, ast->right);
bool_t is_private = (type == PRIVATE_REGION);
gen_name(ast->left);
if (is_private) {
gen_printf(" PRIVATE");
}
}
static void gen_region_list(ast_node *ast) {
Contract(is_ast_region_list(ast));
while (ast) {
gen_region_spec(ast->left);
if (ast->right) {
gen_printf(", ");
}
ast = ast->right;
}
}
static void gen_declare_deployable_region_stmt(ast_node *ast) {
Contract(is_ast_declare_deployable_region_stmt(ast));
gen_printf("@DECLARE_DEPLOYABLE_REGION ");
gen_name(ast->left);
if (ast->right) {
gen_printf(" USING ");
gen_region_list(ast->right);
}
}
static void gen_declare_schema_region_stmt(ast_node *ast) {
Contract(is_ast_declare_schema_region_stmt(ast));
gen_printf("@DECLARE_SCHEMA_REGION ");
gen_name(ast->left);
if (ast->right) {
gen_printf(" USING ");
gen_region_list(ast->right);
}
}
static void gen_begin_schema_region_stmt(ast_node *ast) {
Contract(is_ast_begin_schema_region_stmt(ast));
gen_printf("@BEGIN_SCHEMA_REGION ");
gen_name(ast->left);
}
static void gen_end_schema_region_stmt(ast_node *ast) {
Contract(is_ast_end_schema_region_stmt(ast));
gen_printf("@END_SCHEMA_REGION");
}
static void gen_schema_unsub_stmt(ast_node *ast) {
Contract(is_ast_schema_unsub_stmt(ast));
EXTRACT_ANY_NOTNULL(l, ast->left);
gen_printf("@UNSUB(");
gen_version_and_proc(l);
gen_printf(")");
}
static void gen_schema_resub_stmt(ast_node *ast) {
Contract(is_ast_schema_resub_stmt(ast));
EXTRACT_ANY_NOTNULL(l, ast->left);
gen_printf("@RESUB(");
gen_version_and_proc(l);
gen_printf(")");
}
static void gen_schema_ad_hoc_migration_stmt(ast_node *ast) {
Contract(is_ast_schema_ad_hoc_migration_stmt(ast));
EXTRACT_ANY_NOTNULL(l, ast->left);
EXTRACT_ANY(r, ast->right);
// two arg version is a recreate upgrade instruction
if (r) {
EXTRACT_STRING(group, l);
EXTRACT_STRING(proc, r);
gen_printf("@SCHEMA_AD_HOC_MIGRATION FOR @RECREATE(");
gen_printf("%s, %s)", group, proc);
}
else {
gen_printf("@SCHEMA_AD_HOC_MIGRATION(");
gen_version_and_proc(l);
gen_printf(")");
}
}
static void gen_emit_group_stmt(ast_node *ast) {
Contract(is_ast_emit_group_stmt(ast));
EXTRACT(name_list, ast->left);
gen_printf("@EMIT_GROUP");
if (name_list) {
gen_printf(" ");
gen_name_list(name_list);
}
}
static void gen_emit_enums_stmt(ast_node *ast) {
Contract(is_ast_emit_enums_stmt(ast));
EXTRACT(name_list, ast->left);
gen_printf("@EMIT_ENUMS");
if (name_list) {
gen_printf(" ");
gen_name_list(name_list);
}
}
static void gen_emit_constants_stmt(ast_node *ast) {
Contract(is_ast_emit_constants_stmt(ast));
EXTRACT_NOTNULL(name_list, ast->left);
gen_printf("@EMIT_CONSTANTS ");
gen_name_list(name_list);
}
static void gen_conflict_target(ast_node *ast) {
Contract(is_ast_conflict_target(ast));
EXTRACT(indexed_columns, ast->left);
EXTRACT(opt_where, ast->right);
gen_printf("\nON CONFLICT ");
if (indexed_columns) {
gen_printf("(");
gen_indexed_columns(indexed_columns);
gen_printf(") ");
}
if (opt_where) {
gen_opt_where(opt_where);
gen_printf(" ");
}
}
static void gen_upsert_update(ast_node *ast) {
Contract(is_ast_upsert_update(ast));
EXTRACT_NOTNULL(conflict_target, ast->left);
EXTRACT(update_stmt, ast->right);
gen_conflict_target(conflict_target);
gen_printf("DO ");
if (update_stmt) {
gen_update_stmt(update_stmt);
} else {
gen_printf("NOTHING");
}
}
static void gen_upsert_stmt(ast_node *ast) {
Contract(is_ast_upsert_stmt(ast));
EXTRACT_NOTNULL(insert_stmt, ast->left);
EXTRACT_NOTNULL(upsert_update, ast->right);
gen_insert_stmt(insert_stmt);
gen_upsert_update(upsert_update);
}
static void gen_with_upsert_stmt(ast_node *ast) {
Contract(is_ast_with_upsert_stmt(ast));
EXTRACT_ANY_NOTNULL(with_prefix, ast->left)
EXTRACT_NOTNULL(upsert_stmt, ast->right);
gen_with_prefix(with_prefix);
gen_upsert_stmt(upsert_stmt);
}
static void gen_explain_stmt(ast_node *ast) {
Contract(is_ast_explain_stmt(ast));
EXTRACT_OPTION(query_plan, ast->left);
EXTRACT_ANY_NOTNULL(stmt_target, ast->right);
gen_printf("EXPLAIN");
if (query_plan == EXPLAIN_QUERY_PLAN) {
gen_printf(" QUERY PLAN");
}
gen_printf("\n");
gen_one_stmt(stmt_target);
}
cql_data_defn( int32_t gen_stmt_level );
static void gen_stmt_list(ast_node *root) {
if (!root) {
return;
}
gen_stmt_level++;
int32_t indent_level = (gen_stmt_level > 1) ? 2 : 0;
BEGIN_INDENT(statement, indent_level);
for (ast_node *semi = root; semi; semi = semi->right) {
if (gen_stmt_level == 1 && semi != root) {
gen_printf("\n");
}
EXTRACT_STMT_AND_MISC_ATTRS(stmt, misc_attrs, semi);
if (misc_attrs) {
gen_misc_attrs(misc_attrs);
}
gen_one_stmt(stmt);
if (gen_stmt_level == 0 && semi->right == NULL) {
gen_printf(";");
}
else {
gen_printf(";\n");
}
}
END_INDENT(statement);
gen_stmt_level--;
}
cql_noexport void gen_one_stmt(ast_node *stmt) {
symtab_entry *entry = symtab_find(gen_stmts, stmt->type);
// These are all the statements there are, we have to find it in this table
// or else someone added a new statement and it isn't supported yet.
Invariant(entry);
((void (*)(ast_node*))entry->val)(stmt);
}
// so the name doesn't otherwise conflict in the amalgam
#undef output
#undef STMT_INIT
#define STMT_INIT(x) symtab_add(gen_stmts, k_ast_ ## x, (void *)gen_ ## x)
#undef EXPR_INIT
#define EXPR_INIT(x, func, str, pri_new) \
static gen_expr_dispatch expr_disp_ ## x = { func, str, pri_new }; \
symtab_add(gen_exprs, k_ast_ ## x, (void *)&expr_disp_ ## x);
cql_noexport void gen_init() {
gen_stmts = symtab_new();
gen_exprs = symtab_new();
STMT_INIT(if_stmt);
STMT_INIT(guard_stmt);
STMT_INIT(while_stmt);
STMT_INIT(switch_stmt);
STMT_INIT(leave_stmt);
STMT_INIT(continue_stmt);
STMT_INIT(return_stmt);
STMT_INIT(rollback_return_stmt);
STMT_INIT(commit_return_stmt);
STMT_INIT(call_stmt);
STMT_INIT(declare_out_call_stmt);
STMT_INIT(declare_vars_type);
STMT_INIT(let_stmt);
STMT_INIT(assign);
STMT_INIT(set_from_cursor);
STMT_INIT(create_proc_stmt);
STMT_INIT(trycatch_stmt);
STMT_INIT(throw_stmt);
STMT_INIT(create_trigger_stmt);
STMT_INIT(create_table_stmt);
STMT_INIT(create_virtual_table_stmt);
STMT_INIT(drop_table_stmt);
STMT_INIT(drop_view_stmt);
STMT_INIT(drop_index_stmt);
STMT_INIT(drop_trigger_stmt);
STMT_INIT(alter_table_add_column_stmt);
STMT_INIT(create_index_stmt);
STMT_INIT(create_view_stmt);
STMT_INIT(select_stmt);
STMT_INIT(with_select_stmt);
STMT_INIT(delete_stmt);
STMT_INIT(with_delete_stmt);
STMT_INIT(update_stmt);
STMT_INIT(update_cursor_stmt);
STMT_INIT(with_update_stmt);
STMT_INIT(insert_stmt);
STMT_INIT(with_insert_stmt);
STMT_INIT(upsert_stmt);
STMT_INIT(with_upsert_stmt);
STMT_INIT(upsert_update);
STMT_INIT(conflict_target);
STMT_INIT(fetch_values_stmt);
STMT_INIT(set_blob_from_cursor_stmt);
STMT_INIT(fetch_cursor_from_blob_stmt);
STMT_INIT(declare_enum_stmt);
STMT_INIT(declare_const_stmt);
STMT_INIT(declare_group_stmt);
STMT_INIT(declare_cursor);
STMT_INIT(declare_cursor_like_name);
STMT_INIT(declare_cursor_like_select);
STMT_INIT(declare_named_type);
STMT_INIT(declare_value_cursor);
STMT_INIT(declare_proc_stmt);
STMT_INIT(declare_proc_no_check_stmt);
STMT_INIT(declare_func_stmt);
STMT_INIT(declare_select_func_stmt);
STMT_INIT(loop_stmt);
STMT_INIT(fetch_stmt);
STMT_INIT(fetch_call_stmt);
STMT_INIT(begin_trans_stmt);
STMT_INIT(commit_trans_stmt);
STMT_INIT(rollback_trans_stmt);
STMT_INIT(proc_savepoint_stmt);
STMT_INIT(savepoint_stmt);
STMT_INIT(release_savepoint_stmt);
STMT_INIT(close_stmt);
STMT_INIT(out_stmt);
STMT_INIT(out_union_stmt);
STMT_INIT(echo_stmt);
STMT_INIT(schema_upgrade_version_stmt);
STMT_INIT(schema_upgrade_script_stmt);
STMT_INIT(previous_schema_stmt);
STMT_INIT(enforce_strict_stmt);
STMT_INIT(enforce_normal_stmt);
STMT_INIT(enforce_reset_stmt);
STMT_INIT(enforce_push_stmt);
STMT_INIT(enforce_pop_stmt);
STMT_INIT(declare_schema_region_stmt);
STMT_INIT(declare_deployable_region_stmt);
STMT_INIT(begin_schema_region_stmt);
STMT_INIT(end_schema_region_stmt);
STMT_INIT(schema_ad_hoc_migration_stmt);
STMT_INIT(schema_unsub_stmt);
STMT_INIT(schema_resub_stmt);
STMT_INIT(explain_stmt);
STMT_INIT(emit_enums_stmt);
STMT_INIT(emit_group_stmt);
STMT_INIT(emit_constants_stmt);
EXPR_INIT(num, gen_expr_num, "NUM", EXPR_PRI_ROOT);
EXPR_INIT(str, gen_expr_str, "STR", EXPR_PRI_ROOT);
EXPR_INIT(blob, gen_expr_blob, "BLB", EXPR_PRI_ROOT);
EXPR_INIT(null, gen_expr_null, "NULL", EXPR_PRI_ROOT);
EXPR_INIT(dot, gen_expr_dot, "DOT", EXPR_PRI_ROOT);
EXPR_INIT(const, gen_expr_const, "CONST", EXPR_PRI_ROOT);
EXPR_INIT(bin_and, gen_binary, "&", EXPR_PRI_BINARY);
EXPR_INIT(bin_or, gen_binary, "|", EXPR_PRI_BINARY);
EXPR_INIT(lshift, gen_binary, "<<", EXPR_PRI_BINARY);
EXPR_INIT(rshift, gen_binary, ">>", EXPR_PRI_BINARY);
EXPR_INIT(mul, gen_binary, "*", EXPR_PRI_MUL);
EXPR_INIT(div, gen_binary, "/", EXPR_PRI_MUL);
EXPR_INIT(mod, gen_binary, "%", EXPR_PRI_MUL);
EXPR_INIT(add, gen_binary, "+", EXPR_PRI_ADD);
EXPR_INIT(sub, gen_binary, "-", EXPR_PRI_ADD);
EXPR_INIT(not, gen_unary, "NOT ", EXPR_PRI_NOT);
EXPR_INIT(tilde, gen_unary, "~", EXPR_PRI_TILDE);
EXPR_INIT(collate, gen_binary, "COLLATE", EXPR_PRI_COLLATE);
EXPR_INIT(uminus, gen_uminus, "-", EXPR_PRI_TILDE);
EXPR_INIT(eq, gen_binary, "=", EXPR_PRI_EQUALITY);
EXPR_INIT(lt, gen_binary, "<", EXPR_PRI_INEQUALITY);
EXPR_INIT(gt, gen_binary, ">", EXPR_PRI_INEQUALITY);
EXPR_INIT(ne, gen_binary, "<>", EXPR_PRI_INEQUALITY);
EXPR_INIT(ge, gen_binary, ">=", EXPR_PRI_INEQUALITY);
EXPR_INIT(le, gen_binary, "<=", EXPR_PRI_INEQUALITY);
EXPR_INIT(call, gen_expr_call, "CALL", EXPR_PRI_ROOT);
EXPR_INIT(window_func_inv, gen_expr_window_func_inv, "WINDOW-FUNC-INV", EXPR_PRI_ROOT);
EXPR_INIT(raise, gen_expr_raise, "RAISE", EXPR_PRI_ROOT);
EXPR_INIT(between, gen_expr_between, "BETWEEN", EXPR_PRI_BETWEEN);
EXPR_INIT(not_between, gen_expr_not_between, "NOT BETWEEN", EXPR_PRI_BETWEEN);
EXPR_INIT(and, gen_binary, "AND", EXPR_PRI_AND);
EXPR_INIT(between_rewrite, gen_expr_between_rewrite, "BETWEEN", EXPR_PRI_BETWEEN);
EXPR_INIT(or, gen_binary, "OR", EXPR_PRI_OR);
EXPR_INIT(select_stmt, gen_expr_select, "SELECT", EXPR_PRI_ROOT);
EXPR_INIT(select_if_nothing_throw_expr, gen_expr_select_if_nothing_throw, "IF NOTHING THROW", EXPR_PRI_ROOT);
EXPR_INIT(select_if_nothing_expr, gen_expr_select_if_nothing, "IF NOTHING", EXPR_PRI_ROOT);
EXPR_INIT(select_if_nothing_or_null_expr, gen_expr_select_if_nothing, "IF NOTHING OR NULL", EXPR_PRI_ROOT);
EXPR_INIT(with_select_stmt, gen_expr_select, "WITH...SELECT", EXPR_PRI_ROOT);
EXPR_INIT(is, gen_binary, "IS", EXPR_PRI_EQUALITY);
EXPR_INIT(is_not, gen_binary, "IS NOT", EXPR_PRI_EQUALITY);
EXPR_INIT(is_true, gen_postfix, "IS TRUE", EXPR_PRI_EQUALITY);
EXPR_INIT(is_false, gen_postfix, "IS FALSE", EXPR_PRI_EQUALITY);
EXPR_INIT(is_not_true, gen_postfix, "IS NOT TRUE", EXPR_PRI_EQUALITY);
EXPR_INIT(is_not_false, gen_postfix, "IS NOT FALSE", EXPR_PRI_EQUALITY);
EXPR_INIT(like, gen_binary, "LIKE", EXPR_PRI_EQUALITY);
EXPR_INIT(not_like, gen_binary, "NOT LIKE", EXPR_PRI_EQUALITY);
EXPR_INIT(match, gen_binary, "MATCH", EXPR_PRI_EQUALITY);
EXPR_INIT(not_match, gen_binary, "NOT MATCH", EXPR_PRI_EQUALITY);
EXPR_INIT(regexp, gen_binary, "REGEXP", EXPR_PRI_EQUALITY);
EXPR_INIT(not_regexp, gen_binary, "NOT REGEXP", EXPR_PRI_EQUALITY);
EXPR_INIT(glob, gen_binary, "GLOB", EXPR_PRI_EQUALITY);
EXPR_INIT(not_glob, gen_binary, "NOT GLOB", EXPR_PRI_EQUALITY);
EXPR_INIT(in_pred, gen_expr_in_pred, "IN", EXPR_PRI_EQUALITY);
EXPR_INIT(not_in, gen_expr_not_in, "NOT IN", EXPR_PRI_EQUALITY);
EXPR_INIT(case_expr, gen_expr_case, "CASE", EXPR_PRI_ROOT);
EXPR_INIT(exists_expr, gen_expr_exists, "EXISTS", EXPR_PRI_ROOT);
EXPR_INIT(cast_expr, gen_expr_cast, "CAST", EXPR_PRI_ROOT);
EXPR_INIT(concat, gen_concat, "||", EXPR_PRI_CONCAT);
}
cql_export void gen_cleanup() {
SYMTAB_CLEANUP(gen_stmts);
SYMTAB_CLEANUP(gen_exprs);
gen_output = NULL;
gen_callbacks = NULL;
used_alias_syms = NULL;
}
#endif