sources/cql.h (173 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.
*/
// cql - prounounced "see-queue-el" is a basic tool for enabling stored
// procedures for SQLite. The tool does this by parsing a language
// not unlike typical SQL stored procedure forms available in
// MySql and SQL Server.
//
// Broadly speaaking compilation is as follows:
// * SQL statements such as SELECT/INSERT/UPDATE/DELETE
// are converted into calls to SQLite to do the work.
// Any variables in those statements are converted into
// the appropriate binding and and results are read out
// with the usual SQLite column reading.
// * Stored procedure control flow is converted into the equivalent
// C directly. So for instance an 'IF' in the SQL becomes
// a correlated 'if' in the generated code.
//
// The result of this is that CQL produces, "The C you could have
// written yourself using the SQLite API to do that database operation."
// CQL does this in a less brittle and type-safe way that is far
// more maintainable.
//
// Design principles:
//
// 1. Keep each pass in one file (simple, focused, and easy refactor)
// 2. Use simple printable AST parse nodes (no separate #define per AST node type)
// 3. 100% coverage of all logic, no exceptions.
#pragma once
#include "diags.h"
#include <assert.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#ifndef CQL_AMALGAM
// as well as the integration points.
#define cql_noexport extern
#define cql_export extern
#define cql_data_decl(x) extern x
#define cql_data_defn(x) x
#endif
typedef uint8_t bool_t;
typedef long long int llint_t;
#include "compat.h"
#define u32_not(x) ((uint32_t)(~(x)))
#define u64_not(x) ((uint64_t)(~(x)))
#if LONG_MAX > 0x7fffffff
#define _64(x) x##L
#else
#define _64(x) x##LL
#endif
// patternlint-disable-next-line prefer-sized-ints-in-msys
int main(int argc, char **argv);
// we need this for some callbacks
struct charbuf;
typedef struct cmd_options {
bool_t test;
bool_t echo_input;
bool_t print_ast;
bool_t print_dot;
bool_t semantic;
bool_t codegen;
bool_t compress;
bool_t generate_type_getters;
bool_t generate_exports;
bool_t run_unit_tests;
bool_t nolines;
bool_t schema_exclusive;
char *rt;
char **file_names;
int32_t file_names_count;
char **include_regions;
int32_t include_regions_count;
char **exclude_regions;
int32_t exclude_regions_count;
int32_t min_schema_version;
char *objc_c_include_path;
char *c_include_namespace;
char *java_class_under_test;
char *java_package_name;
char *java_assembly_query_classname;
char **java_fragment_interfaces;
int32_t java_fragment_interfaces_count;
bool_t java_fragment_interface_mode;
char **java_imports;
int32_t java_imports_count;
char *cqlrt;
bool_t dev; // option use to activate features in development or dev features
} cmd_options;
cql_data_decl( cmd_options options );
#define Invariant assert
#define Contract assert
#define _new(x) ((x*)malloc(sizeof(x)))
#define _new_array(x,c) ((x*)malloc(c*sizeof(x)))
#define CQL_NICE_LITERAL_NAME_LIMIT 32
// note this is not easily changed, storage for used strach variables is in an unsigned long long
#define CQL_MAX_STACK 128
typedef const char *CSTR;
typedef enum cg_symbol_case {
cg_symbol_case_snake,
cg_symbol_case_pascal,
cg_symbol_case_camel,
} cg_symbol_case;
cql_data_decl( const char *global_proc_name );
typedef struct ast_node *ast_ptr;
typedef struct rtdata {
// the command line name of this result type
const char *name;
// The main code generator function that will be executed.
void (*code_generator)(ast_ptr root);
// The number of file names required by the rt. Use -1 for a variable number
// of file names that will be verified by the code generator itself based on
// the arguments passed t it
int32_t required_file_names_count;
// A string to add before any header contents (include copyright, autogen comments, runtime include, etc).
const char *header_prefix;
// The default "cqlrt.h" for this code type
const char *cqlrt;
// the formatting string into which the filename above is placed
const char *cqlrt_template;
// A begin string to wrap the contents of the header file.
const char *header_wrapper_begin;
// A end string to wrap the contents of the header file.
const char *header_wrapper_end;
// A string to add before any source contents (include copyright, autogen comments, etc).
const char *source_prefix;
// A begin string to wrap the contents of the source file.
const char *source_wrapper_begin;
// A end string to wrap the contents of the source file.
const char *source_wrapper_end;
// A string to add before any import file contents (include copyright, autgen comments, etc).
const char *exports_prefix;
// The case to use for symbols.
cg_symbol_case symbol_case;
// If enabled, generic type-based getters are used by the generated code, registering the callback function
// pointers when creating the result set objects.
bool_t generate_type_getters;
// If enabled, macros will be generated to test equality between 2 list/index pairs.
bool_t generate_equality_macros;
// Called for each proc name that is processed.
bool_t (*register_proc_name)(const char *proc_name);
// Predicate function to determine whether to implicitly generate the copy function for a result set.
// The cql:generate_copy attribute overrides the value, if specified.
bool_t (*proc_should_generate_copy)(const char *proc_name);
// Provides a chance to add some extra definitions to the result set type, specify if extra stuff needed.
bool_t (*result_set_type_decl_extra)(struct charbuf *output, CSTR sym, CSTR ref);
// Prefix for public symbol.
const char *symbol_prefix;
// Prefix for private implementation symbol.
const char *impl_symbol_prefix;
// Visibility attribute for generated functions.
const char *symbol_visibility;
// Assertion macro for API contract violations.
const char *cql_contract;
// Logging database error;
const char *cql_log_database_error;
// The type for a hash code.
const char *cql_hash_code;
// The type for a boolean value.
const char *cql_bool;
// The type for a 32-bit integer value.
const char *cql_int32;
// The type for a 64-bit integer value.
const char *cql_int64;
// The type for a double value.
const char *cql_double;
// The type for a sqlite3 result code.
const char *cql_code;
// The type for an object ref.
const char *cql_object_ref;
// Adds a reference count to the object.
// @param obj The object to be retained.
// void cql_object_retain(cql_object_ref _Nullable obj);
const char *cql_object_retain;
// Subtracts a reference count from the object. When it reaches 0, the object SHOULD be freed.
// @param str The object to be released.
// void cql_object_release(cql_object_ref _Nullable obj);
const char *cql_object_release;
// The type for a blob ref.
const char *cql_blob_ref;
// Get size of a blob ref.
const char *cql_get_blob_size;
// Adds a reference count to the blob.
// @param blob The blob to be retained.
// void cql_blob_retain(cql_blob_ref _Nullable blob);
const char *cql_blob_retain;
// Subtracts a reference count from the blob. When it reaches 0, the blob SHOULD be freed.
// @param str The blob to be released.
// void cql_blob_release(cql_blob_ref _Nullable blob);
const char *cql_blob_release;
// The type for a string object.
const char *cql_string_ref;
// Construct a new string object.
// @param cstr The C string to be stored.
// @return A string object of the type defined by cql_string_ref.
// cql_string_ref cql_string_ref_new(const char *cstr);
const char *cql_string_ref_new;
// The encode type for a string object.
const char *cql_string_ref_encode;
// The include library for the encode type for a string object.
const char *cql_string_ref_encode_include;
// Declare a static const string literal object. This must be a global object
// and will be executed in the global context.
// NOTE: This MUST be implemented as a macro as it both declares and assigns
// the value.
// @param name The name of the object.
// @param text The text to be stored in the object.
// cql_string_literal(cql_string_ref name, const char *text);
const char *cql_string_literal;
// Declare a const string that holds the name of a stored procedure. This must
// be a global object and will be executed in the global context.
// NOTE: This MUST be implemented as a macro as it both declares and assigns
// the value.
// @param name The name of the object.
// @param proc_name The procedure name to be stored in the object.
// cql_string_literal(cql_string_ref name, const char *proc_name);
const char *cql_string_proc_name;
// Adds a reference count to the string object.
// @param str The string object to be retained.
// void cql_string_retain(cql_string_ref _Nullable str);
const char *cql_string_retain;
// Subtracts a reference count from the string object. When it reaches 0, the string SHOULD be freed.
// @param str The string object to be released.
// void cql_string_release(cql_string_ref _Nullable str);
const char *cql_string_release;
// Creates a hash code for the string object.
// @param str The string object to be hashed.
// cql_hash_code cql_string_hash(cql_string_ref _Nullable str);
const char *cql_string_hash;
// Creates a hash code for the blob object.
// @param blob The blob object to be hashed.
// cql_hash_code cql_blob_hash(cql_string_ref _Nullable str);
const char *cql_blob_hash;
// Checks if two blob objects are equal.
// NOTE: If both objects are NULL, they are equal; if only 1 is NULL, they are not equal.
// @param str1 The first blob to compare.
// @param str2 The second blob to compare.
// @return cql_true if they are equal, otherwise cql_false.
// cql_bool cql_blob_equal(cql_blob_ref _Nullable bl1, cql_blob_ref _Nullable bl2);
const char *cql_blob_equal;
// Compares two string objects.
// @param str1 The first string to compare.
// @param str2 The second string to compare.
// @return < 0 if str1 is less than str2, > 0 if str2 is less than str1, = 0 if str1 is equal to str2.
// int cql_string_compare(cql_string_ref str1, cql_string_ref str2);
const char *cql_string_compare;
// Checks if two string objects are equal.
// NOTE: If both objects are NULL, they are equal; if only 1 is NULL, they are not equal.
// @param str1 The first string to compare.
// @param str2 The second string to compare.
// @return cql_true if they are equal, otherwise cql_false.
// cql_bool cql_string_equal(cql_string_ref _Nullable str1, cql_string_ref _Nullable str2);
const char *cql_string_equal;
// Compares two string objects with SQL LIKE semantics.
// NOTE: If either object is NULL, the result should be 1.
// @param str1 The first string to compare.
// @param str2 The second string to compare.
// @return 0 if the str1 is LIKE str2, else != 0.
// int cql_string_like(cql_string_ref str1, cql_string_ref str2);
const char *cql_string_like;
// Declare and allocate a C string from a string object.
// NOTE: This MUST be implemented as a macro, as it both declares and assigns the value.
// @param cstr The C string var to be declared and assigned.
// @param str The string object that contains the string value.
// cql_alloc_cstr(const char *cstr, cql_string_ref str);
const char *cql_alloc_cstr;
// Free a C string that was allocated by cql_alloc_cstr
// @param cstr The C string to be freed.
// @param str The string object that the C string was allocated from.
// cql_free_cstr(const char *cstr, cql_string_ref str);
const char *cql_free_cstr;
// The type for a generic cql result set.
// NOTE: Result sets are cast to this type before being passed to the cql_result_set_get_count/_data functions.
const char *cql_result_set_ref;
// Construct a new result set object.
// @param data The data to be stored in the result set.
// @param count The count of records represented by the data in the result_set.
// @param columns The number of columns for this result type.
// @param dataTypes The data types for the columns.
// @param callbacks The callbacks that are used for the data access.
// @return A result_set object of the type.
// cql_result_set_ref _Nonnull cql_result_set_ref_new(
// void *_Nonnull data,
// cql_int32 count,
// void (*_Nonnull teardown)(cql_result_set_ref _Nonnull result_set),
// uint8_t *_Nonnull dataTypes,
// cql_result_set_meta_struct meta);
const char *cql_result_set_ref_new;
// The name of the struct for all of the metadata passed to cql_result_set_ref_new. The struct must have the
// following fields, by name. Any additional fields may be added for internal support of the runtime.
//
// teardown: void (*_Nullable)(cql_result_set_ref _Nonnull result_set)
// copy: void (*_Nullable)(
// cql_result_set_ref _Nonnull result_set,
// cql_result_set_ref _Nullable *_Nonnull to_result_set,
// cql_int32 from,
// cql_int32 count)
// refOffsets: unsigned short *refOffsets (offsets to all of the references in a row)
// rowsize: size_t rowsize (the size of each row in bytes)
//
// The following getter callbacks are only used when generate_type_getters is true.
// getBoolean: Boolean (*_Nullable)(cql_result_set_ref _Nonnull result_set, cql_int32 row, cql_int32 col)
// getDouble: double (*_Nullable)(cql_result_set_ref _Nonnull result_set, cql_int32 row, cql_int32 col)
// getInt32: int32_t (*_Nullable)(cql_result_set_ref _Nonnull result_set, cql_int32 row, cql_int32 col)
// getInt64: int64_t (*_Nullable)(cql_result_set_ref _Nonnull result_set, cql_int32 row, cql_int32 col)
// getString: cql_string_ref _Nonnull (*_Nullable)(
// cql_result_set_ref _Nonnull result_set,
// cql_int32 row,
// cql_int32 col)
// getIsNull: Boolean (*_Nullable)(cql_result_set_ref _Nonnull result_set, cql_int32 row, cql_int32 col)
// getIsEncoded: Boolean (*_Nullable)(cql_result_set_ref _Nonnull result_set, cql_int32 col)
const char *cql_result_set_meta_struct;
// The name of the method that will give the metadata struct back as provided to the construction above
const char *cql_result_set_get_meta;
// Adds a reference count to the result_set object.
// NOTE: This MUST be implemented as a macro, as it takes a result set as a param, which has an undefined type.
// @param result_set The result set object to be retained.
// void cql_result_set_retain(** _Nullable result_set);
const char *cql_result_set_retain;
// Subtracts a reference count from the result_set object. When it reaches 0, the result_set SHOULD be freed.
// NOTE: This MUST be implemented as a macro, as it takes a result set as a param, which has an undefined type.
// @param result_set The result set object to be released.
// void cql_result_set_release(** _Nullable result_set);
const char *cql_result_set_release;
// Accounts for a transfer of ownership of the result_set object by decrementing its reference count.
// @param result_set The result set object whose reference count should be decremented.
// void cql_result_set_note_ownership_transferred(** _Nullable result_set);
const char *cql_result_set_note_ownership_transferred;
// Get the count of the query data.
// NOTE: This MUST be implemented as a macro, as it takes a result set as a param, which has an undefined type.
// @param result_set The cql result set object.
// @return The count that was previous stored on the result set.
// cql_int32 cql_result_set_get_count(** result_set);
const char *cql_result_set_get_count;
// Retrieve the storage of the query data.
// NOTE: This MUST be implemented as a macro, as it takes a result set as a param, which has an undefined type.
// @param result_set The cql result_set object.
// @return The data that was previous stored on the result set.
// void *cql_result_set_get_data(** result_set)
const char *cql_result_set_get_data;
// Generic bool value getter on base result set object.
// NOTE: This is only used when generate_type_getters is true. This function should call through to the
// inline type getters that are passed into the ctor for the result set.
// @param result_set The cql result_set object.
// @param row The row number to fetch the value for.
// @param col The column to fetch the value for.
// @return The bool value.
// cql_bool cql_result_set_get_bool(cql_result_set_ref result_set, int32_t row, int32_t col)
const char *cql_result_set_get_bool;
// Generic double value getter on base result set object.
// NOTE: This is only used when generate_type_getters is true. This function should call through to the
// inline type getters that are passed into the ctor for the result set.
// @param result_set The cql result_set object.
// @param row The row number to fetch the value for.
// @param col The column to fetch the value for.
// @return The double value.
// cql_double cql_result_set_get_double(cql_result_set_ref result_set, int32_t row, int32_t col)
const char *cql_result_set_get_double;
// Generic int32 value getter on base result set object.
// NOTE: This is only used when generate_type_getters is true. This function should call through to the
// inline type getters that are passed into the ctor for the result set.
// @param result_set The cql result_set object.
// @param row The row number to fetch the value for.
// @param col The column to fetch the value for.
// @return The int32 value.
// cql_int32 cql_result_set_get_int32(cql_result_set_ref result_set, int32_t row, int32_t col)
const char *cql_result_set_get_int32;
// Generic int64 value getter on base result set object.
// NOTE: This is only used when generate_type_getters is true. This function should call through to the
// inline type getters that are passed into the ctor for the result set.
// @param result_set The cql result_set object.
// @param row The row number to fetch the value for.
// @param col The column to fetch the value for.
// @return The int64 value.ali
// cql_int64 cql_result_set_get_int64(cql_result_set_ref result_set, int32_t row, int32_t col)
const char *cql_result_set_get_int64;
// Generic string value getter on base result set object.
// NOTE: This is only used when generate_type_getters is true. This function should call through to the
// inline type getters that are passed into the ctor for the result set.
// @param result_set The cql result_set object.
// @param row The row number to fetch the value for.
// @param col The column to fetch the value for.
// @return The string value.
// cql_string_ref _Nullable cql_result_set_get_string(cql_result_set_ref result_set, int32_t row, int32_t col)
const char *cql_result_set_get_string;
// Generic object value getter on base result set object.
// NOTE: This is only used when generate_type_getters is true. This function should call through to the
// inline type getters that are passed into the ctor for the result set.
// @param result_set The cql result_set object.
// @param row The row number to fetch the value for.
// @param col The column to fetch the value for.
// @return The object value.
// cql_object_ref _Nullable cql_result_set_get_object(cql_result_set_ref result_set, int32_t row, int32_t col)
const char *cql_result_set_get_object;
// Generic blob value getter on base result set object.
// NOTE: This is only used when generate_type_getters is true. This function should call through to the
// inline type getters that are passed into the ctor for the result set.
// @param result_set The cql result_set object.
// @param row The row number to fetch the value for.
// @param col The column to fetch the value for.
// @return The string value.
// cql_blob_ref _Nullable cql_result_set_get_blob(cql_result_set_ref result_set, int32_t row, int32_t col)
const char *cql_result_set_get_blob;
// Generic is_null value getter on base result set object.
// NOTE: This is only used when generate_type_getters is true. This function should call through to the
// inline type getters that are passed into the ctor for the result set.
// @param result_set The cql result_set object.
// @param row The row number to fetch the value for.
// @param col The column to fetch the value for.
// @return cql_true if the value is null, otherwise cql_false.
// cql_bool cql_result_set_get_is_null(cql_result_set_ref result_set, int32_t row, int32_t col)
const char *cql_result_set_get_is_null;
// Generic is_encoded value getter on base result set object.
// NOTE: This is only used when generate_type_getters is true. This function should call through to the
// inline type getters that are passed into the ctor for the result set.
// @param result_set The cql result_set object.
// @param col The column to fetch the value for.
// @return cql_true if the value is sensitive, otherwise cql_false.
// cql_bool cql_result_set_get_is_encoded(cql_result_set_ref result_set, int32_t col)
const char *cql_result_set_get_is_encoded;
// Generic bool value setter on base result set object.
// @param result_set The cql result_set object.
// @param row The row number to set the value for.
// @param col The column to set the value for.
// @param new_value the new boolean value to be set.
// void cql_result_set_set_bool(cql_result_set_ref result_set, int32_t row, int32_t col, cql_bool new_value)
const char *cql_result_set_set_bool;
// Generic double value setter on base result set object.
// @param result_set The cql result_set object.
// @param row The row number to set the value for.
// @param col The column to set the value for.
// @param new_value the new boolean value to be set.
// void cql_result_set_set_double(cql_result_set_ref result_set, int32_t row, int32_t col, double new_value)
const char *cql_result_set_set_double;
// Generic cql_int32 value setter on base result set object.
// @param result_set The cql result_set object.
// @param row The row number to set the value for.
// @param col The column to set the value for.
// @param new_value the new cql_int32 value to be set.
// void cql_result_set_set_int32(cql_result_set_ref result_set, int32_t row, int32_t col, cql_int32 new_value)
const char *cql_result_set_set_int32;
// Generic int64 value setter on base result set object.
// @param result_set The cql result_set object.
// @param row The row number to set the value for.
// @param col The column to set the value for.
// @param new_value the new int64 value to be set.
// void cql_result_set_set_int64(cql_result_set_ref result_set, int32_t row, int32_t col, cql_int64 new_value)
const char *cql_result_set_set_int64;
// Generic string value setter on base result set object.
// @param result_set The cql result_set object.
// @param row The row number to set the value for.
// @param col The column to set the value for.
// @param new_value the new string value to be set.
// void cql_result_set_set_string(cql_result_set_ref result_set, int32_t row, int32_t col, cql_string new_value)
const char *cql_result_set_set_string;
// Generic object value setter on base result set object.
// @param result_set The cql result_set object.
// @param row The row number to set the value for.
// @param col The column to set the value for.
// @param new_value the new object value to be set.
// void cql_result_set_set_object(cql_result_set_ref result_set, int32_t row, int32_t col, cql_object new_value)
const char *cql_result_set_set_object;
// Generic blob value setter on base result set object.
// @param result_set The cql result_set object.
// @param row The row number to set the value for.
// @param col The column to set the value for.
// @param new_value the new blob value to be set.
// void cql_result_set_set_blob(cql_result_set_ref result_set, int32_t row, int32_t col, cql_blob new_value)
const char *cql_result_set_set_blob;
// The java type for a nullable boolean value.
const char *cql_bool_nullable;
// The java type for a nullable int value.
const char *cql_int32_nullable;
// The java type for a nullable long value.
const char *cql_int64_nullable;
// The java type for a nullable double value.
const char *cql_double_nullable;
// Template for the java method hasIdentityColumns.
const char *cql_result_set_has_identity_columns;
// Template for the java method copy.
const char *cql_result_set_copy;
} rtdata;
cql_data_decl( rtdata *rt );
cql_noexport void cql_cleanup_and_exit(int32_t code);
// output to "stderr"
cql_noexport void cql_error(const char *format, ...);
// output to "stdout"
cql_noexport void cql_output(const char *format, ...);
// Creates a file in write mode. Aborts if there's any error.
cql_export FILE *cql_open_file_for_write(CSTR file_name);
// Create file, write the data to it, and close the file
cql_export void cql_write_file(const char *file_name, const char *data);
cql_noexport void line_directive(const char *directive);
cql_export void cql_emit_error(const char *err);
cql_export void cql_emit_output(const char *out);
cql_data_decl( char *current_file );
cql_noexport CSTR get_last_doc_comment();