sources/gen_sql.h (61 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.
*/
#pragma once
#include "charbuf.h"
#include "ast.h"
cql_data_decl( int32_t gen_stmt_level );
cql_noexport void gen_init(void);
cql_noexport void gen_cleanup(void);
cql_noexport void gen_set_output_buffer(struct charbuf *_Nonnull buffer);
typedef void (*_Nonnull gen_func)(ast_node *_Nonnull ast);
cql_noexport void gen_stmt_list_to_stdout(ast_node *_Nullable ast);
cql_noexport void gen_select_core(ast_node *_Nonnull ast);
cql_noexport void gen_one_stmt_to_stdout(ast_node *_Nonnull ast);
cql_noexport void gen_misc_attrs_to_stdout(ast_node *_Nonnull ast);
cql_noexport void gen_root_expr(ast_node *_Nonnull ast);
cql_noexport void gen_col_or_key(ast_node *_Nonnull ast);
cql_noexport void gen_params(ast_node *_Nonnull ast);
cql_noexport void gen_declare_proc_from_create_or_decl(ast_node *_Nonnull ast);
cql_noexport void gen_one_stmt(ast_node *_Nonnull stmt);
cql_noexport void gen_misc_attrs(ast_node *_Nonnull ast);
cql_noexport void gen_misc_attr_value(ast_node *_Nonnull ast);
cql_noexport void gen_misc_attr_value_list(ast_node *_Nonnull ast);
cql_noexport void gen_fk_action(int32_t action);
cql_noexport void gen_insert_type(ast_node *_Nonnull ast);
cql_noexport void gen_declare_proc_from_create_proc(ast_node *_Nonnull ast);
cql_noexport void gen_col_key_list(ast_node *_Nonnull list);
// automatically sets the output buffer and printf's the results of the above
cql_noexport void gen_to_stdout(ast_node *_Nullable ast, gen_func fn);
// signature for a callback, you get your context plus the ast
// if you return true then the normal output is suppressed
// in any case the output you provide is emitted
typedef bool_t (*_Nullable gen_sql_callback)(struct ast_node *_Nonnull ast, void *_Nullable context, charbuf *_Nonnull output);
// These modes control the overall style of the output
enum gen_sql_mode {
gen_mode_echo, // Prints everything in the original, with standard whitespace and parentheses
gen_mode_sql, // Prints the AST formatted for SQLite consumption, omits anything CQL specific
gen_mode_no_annotations // Equivalent to gen_mode_echo without versioning attributes or generic attributes
// * @create, @delete, @recreate, and @attribute are removed
// * statements like @echo are not affected, nor is the type specifier @sensitive
};
// Callbacks allow you to significantly alter the generated sql, see the particular flags below.
typedef struct gen_sql_callbacks {
// Each time a local/global variable is encountered in the AST, this callback is invoked
// this is to allow the variable reference to be noted and replaced with ? in the generated SQL
gen_sql_callback _Nullable variables_callback;
void *_Nullable variables_context;
// Each time a column definition is emitted this callback is invoked, it may choose to
// suppress that column. This is used to remove columns that were added in later schema
// versions from the baseline schema.
gen_sql_callback _Nullable col_def_callback;
void *_Nullable col_def_context;
// This callback is used to explain the * in select * or select T.*
gen_sql_callback _Nullable star_callback;
void *_Nullable star_context;
// This callback is used to force the "IF NOT EXISTS" form of DDL statements when generating
// schema upgrade steps. e.g. a "CREATE TABLE Foo declarations get "IF NOT EXISTS" added
// to them in upgrade steps.
gen_sql_callback _Nullable if_not_exists_callback;
void *_Nullable if_not_exists_context;
// This callback is used to allow the caller to rename some table references to other names
// Normally this is used to make replacements in shared fragments
gen_sql_callback _Nullable table_rename_callback;
void *_Nullable table_rename_context;
// This callback is used to expand CALL sequences inside of a CTE expression inline
// the normal response will be to recursively generate the SQL for the procedure
// and emit it to the output stream
gen_sql_callback _Nullable cte_proc_callback;
void *_Nullable cte_proc_context;
// This callback is used to expand inline function call sequences inside of a SQL expression
// the normal response will be to recursively generate the SQL for the function fragment
// and emit it to the output stream
gen_sql_callback _Nullable inline_func_callback;
void *_Nullable inline_func_context;
// This callback is used to suppress any particular CTE that we might need to omit from a select statement
// normally this causes us to check the name of the CTE against a blocklist
gen_sql_callback _Nullable cte_suppress_callback;
void *_Nullable cte_suppress_context;
// If true, hex literals are converted to decimal. This is for JSON which does not support hex literals.
bool_t convert_hex;
// If true casts like "CAST(NULL as TEXT)" are reduced to just NULL. The type information is not needed
// by SQLite so it just wastes space.
bool_t minify_casts;
// If true then unused aliases in select statements are elided to save space. This is safe because
// CQL always binds the top level select statement by ordinal anyway.
bool_t minify_aliases;
// mode to print cql statement: gen_mode_echo, gen_mode_sql, gen_mode_no_annotations.
// gen_mode_sql mode causes the AS part of virtual table to be suppressed
enum gen_sql_mode mode;
// If CQL finds a column such as 'x' below'
//
// create table foo(
// x long_int primary key autoincrement
// );
//
// that column must be converted to this form:
//
// create table foo(
// x integer primary key autoincrement
// );
//
// This is because SQLite mandates that autoincrement must be exactly
// in the second example above however, it is also the case that in SQLite
// an integer can store a 64 bit value. So sending "integer" to SQLite while
// keeping the sense that the column is to be treated as 64 bits in CQL works
// just fine.
//
// However, when we are emitting CQL (rather than SQL) we want to keep
// the original long_int type so as not to lose fidelity when processing
// schema for other semantic checks (such as matching FK data types).
//
// This flag is for that purpose: It tells us that the target isn't SQLite
// and we don't need to do the mapping (yet). Indeed, we shouldn't, or the
// types will be messed up.
//
// In short, if CQL is going to process the output again, use this flag
// to control the autoincrement transform. It might be possible to fold
// this flag with the mode flag but it's sufficiently weird that this
// extra documentation and special handling is probably worth the extra
// boolean storage.
bool_t long_to_int_conv;
} gen_sql_callbacks;
cql_noexport void init_gen_sql_callbacks(gen_sql_callbacks *_Nullable callbacks);
// Generate helper methods
cql_noexport void gen_with_callbacks(ast_node *_Nonnull ast, gen_func fn, gen_sql_callbacks *_Nullable _callbacks);
cql_noexport void gen_col_def_with_callbacks(ast_node *_Nonnull ast, gen_sql_callbacks *_Nullable _callbacks);
cql_noexport void gen_statement_with_callbacks(ast_node *_Nonnull ast, gen_sql_callbacks *_Nullable _callbacks);
cql_noexport bool_t eval_star_callback(ast_node *_Nonnull ast);
cql_noexport bool_t eval_variables_callback(ast_node *_Nonnull ast);
cql_noexport bool_t eval_column_callback(ast_node *_Nonnull ast);