
masell.flabbergast.flabbergast-stdlib.0.19.source-code.sql.o_0 Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of flabbergast-stdlib Show documentation
Show all versions of flabbergast-stdlib Show documentation
The Standard Library for the Flabbergast Configuration Language on the JVM.
The newest version!
Introduction{{{Support for interface with SQL relational databases}}}
utils_lib : From lib:utils
{{{Query an SQL database and produce a frame containing a frame for each row.
The names of the columns, which must be valid Flabbergast identifiers, will be
the attribute names in each “row” frame. The special column name
\Mono{ATTRNAME} will become the attribute name of the “row” frame in the
containing frame, otherwise, the “row” frames will be numbered. A column name
can be prefixed by \Mono{$} to allow lookups. In this case, the column value,
which must be a string, is treated as a lookup, which may contain periods, and
evaluated with contextual lookup semantics.}}}
retrieve : Template (From lib:sqlinterop ).retrieve {
{{{A connection from a \Mono{From sql:} import}}}
connection : Required
{{{A string containing the implementation-specific SQL statement.}}}
sql_query : Required
{{{The template to be used for each row. When the query is evaluated, this
template is expanded for each row and the columns, except \Mono{ATTRNAME},
from the database are overrides on the row.}}}
sql_row_tmpl : Used
}
{{{Create an SQL-statement generating template.}}}
queryifier : Template {
{{{The template to modify.}}}
base : Required
value : Template base {
{{{A frame of expressions or columns to be included in the resulting table.}}}
columns : Required
{{{A collection of expressions (or columns) that must be true to include a
row in the results.}}}
where : Template {}
{{{A collection of expressions (or columns) by which to group the resulting
columns.}}}
group_by : Template {}
{{{The collection of columns available from each of the tables specified in
\{tables}. These can be used in any place that accepts an expression. Given
there is a column \Mono{c} in a table \Mono{t}, reference it as
\Mono{column.t.c}.}}}
column :
For t : table, name : Name
Select name : t.known_columns
{{{Provides templates for construction expressions. See \{sql_provider} for details.}}}
expr : provider.expr
{{{The correct database provider from \{sql_providers}.}}}
provider : Required
{{{Expand the where template.}}}
exp_where : where {}
{{{Expand the group by template.}}}
exp_group : ({ value : group_by {} sql_aggregate_context : "GROUP BY" }).value
{{{A list of tables, inferred from \{columns}, to be included in this
query.}}}
tables :
For column : columns
Reduce For col_table : column.sql_tables, acc_table : acc, name : Name Select name : col_table ?? acc_table
With acc :
(For clause : exp_where
Reduce For where_table : clause.sql_tables, acc_table : acc, name : Name Select name : where_table ?? acc_table
With acc : {})
{{{Generate the \Mono{FROM} clause.}}}
from_clause :
For t : tables, pos : Ordinal
Reduce (If pos > 1 Then acc & ", " Else "") & t
With acc : Null
{{{Generate the columns in the \Mono{SELECT}.}}}
column_clause :
For col : columns, name : Name, pos : Ordinal
Reduce (If pos > 1 Then acc & ", " Else "") & col.sql_expr & " AS " & provider.symbol_start Enforce Str & name & provider.symbol_end Enforce Str
With acc : Null
{{{Generate the \Mono{WHERE} clause, if any.}}}
where_clause :
For clause : exp_where, position : Ordinal
Reduce (If position > 1 Then acc & " AND " Else " WHERE ") & clause.sql_expr
With acc : ""
sql_aggregate_context : Null
sql_non_aggregate_result_cols :
For x : columns Append exp_where
Reduce For acc_cols : acc, x_cols : x.sql_non_aggregate_columns, name : Name Select name : acc_cols ?? x_cols
With acc : {}
sql_group_cols :
For x : exp_group
Reduce For acc_cols : acc, x_cols : x.sql_non_aggregate_columns, name : Name Select name : acc_cols ?? x_cols
With acc : {}
sql_results_contain_aggregate :
For x : columns Append exp_where
Reduce acc || x.sql_contains_aggregate
With acc : False
ungrouped_aggregate_cols :
For group : sql_group_cols, result : sql_non_aggregate_result_cols
Where group Is Null
Reduce acc & " " & result
With acc : ""
group_clause :
If sql_results_contain_aggregate && Length (ungrouped_aggregate_cols Enforce Str) > 0
Then Error ("The following columns are not grouped:" & ungrouped_aggregate_cols)
Else
(For clause : exp_group, position : Ordinal
Reduce (If position > 1 Then acc & ", " Else " GROUP BY ") & clause.sql_expr
With acc : "")
{{{Generate the total query.}}}
sql_query :
If column_clause Is Null Then Error "No columns specified in query."
Else "SELECT " & column_clause Enforce Str & (If from_clause Is Null Then provider.null_from Else " FROM " & from_clause Enforce Str) & where_clause Enforce Str & group_clause Enforce Str
}
}
{{{Query an SQL database for the delicious records within.}}}
query : Template queryifier(base : retrieve) {
{{{A template of templates to generate tables to be embedded in the query. See \{sql_literal_table_tmpl}.}}}
literal_tables : Template {}
{{{A collection of tables provided by the database.}}}
table :
For real : provider.table_discovery(connection : connection), literal : literal_tables {}, name : Name
Select name : real ?? (literal {})
{{{The default set of columns to retreive is all known columns from the selected tables.}}}
columns :
For col : column
Reduce For acc_value : acc, col_value : col, name : Name
Select name : acc_value ?? col_value
With acc : {}
{{{The provider, automatically selected from the database connection.}}}
provider :
((For provider : sql_providers, provider_name : Name
Where provider_name == connection.provider Enforce Str
Reduce provider With acc : Null) ?? (Error "Unsupported database “\(connection.provider Enforce Str)”. This library must be mismatched to the Flabbergast runtime.")) {}
}
{{{Create an SQL query without a connection to the database. The tables and
columns available must be manually curated.}}}
offline_query : queryifier(base : Template { value : sql_query })
{{{The template to create a table for an offline query.}}}
offline_table_tmpl : Template {
{{{The name of the schema this table belongs to.}}}
table_schema : Required
{{{The name of this table in this schema.}}}
table_name : Required
{{{The columns in this table, using the \{column_tmpl}.}}}
columns : Template {}
{{{The template to use for each column.}}}
column_tmpl : Template provider.expr.base_expr_tmpl {
{{{The name of the column.}}}
column_name : Required
{{{The type of the column. If not specified, it is assumed to be a string.}}}
sql_type : sql_types.str
sql_tables : Now utils_lib.frame(sql_from, name : Id)
sql_expr : sql_from Enforce Str & "." & provider.symbol_start Enforce Str & column_name Enforce Str & provider.symbol_end Enforce Str
sql_non_aggregate_columns : utils_lib.frame(sql_expr, name : Id)
sql_contains_aggregate : False
}
known_columns : columns {}
sql_from : (If table_schema Is Null Then "" Else provider.symbol_start Enforce Str & table_schema Enforce Str & provider.symbol_end Enforce Str & ".") & provider.symbol_start Enforce Str & table_name Enforce Str & provider.symbol_end Enforce Str
}
{{{Convert a value into a SQL expression if possible.}}}
sql_marshall : utils_lib.str_concatifier(base : sql_marshall_list, delimiter : "")
{{{Convert a list of Flabbergast items to an SQL expression.}}}
sql_marshall_list : Template {
provider : Required
value :
For arg : args, arg_name : Name
Select arg_name : (TypeOf arg With sql_marshall_converters {}).value
}
sql_marshall_converters : {
bin :
Template {
value : provider.expr.base_const_tmpl {
sql_expr : provider.blob_start Enforce Str & utils_lib.bin_to_hex_str(arg, uppercase : True) Enforce Str & provider.blob_end Enforce Str
sql_type : sql_types.blob
}
}
bool :
Template {
value : provider.expr.base_const_tmpl {
sql_expr : If arg Then "TRUE" Else "FALSE"
sql_type : sql_types.bool
}
}
float :
Template {
value : provider.expr.base_const_tmpl {
sql_expr : arg To Str
sql_type : sql_types.float
}
}
frame : Template { value : arg }
int :
Template {
value : provider.expr.base_const_tmpl {
sql_expr : arg To Str
sql_type : sql_types.int
}
}
null : Template { value : Null }
template : Template { value : Error "Cannot convert template to SQL." }
str :
Template {
value : provider.expr.base_const_tmpl {
sql_expr : provider.quote_start Enforce Str & utils_lib.str_escape(arg, transformations : provider.transformations) Enforce Str & provider.quote_end Enforce Str
sql_type : sql_types.str
}
}
}
{{{Compute the SQL type of a complex expression.}}}
synthetic_sql_type : Template {
coerce : Required
id : 0
cast :
For type : sql_types
Where type Is Frame
Reduce If type.id B& coerce != 0
Then acc B| type.cast
Else acc
With acc : 0
name :
(For type : sql_types
Where type Is Frame && type.id B& coerce != 0
Reduce If acc Is Null
Then type.name Enforce Str
Else acc Enforce Str & " or " & type.name Enforce Str
With acc : Null) ?? "none"
}
{{{A table from Flabbergast to be embedded in the query. It should probably be short.}}}
sql_literal_table_tmpl : Template {
{{{A list of frames to convert to rows in the table. If any attributes are missing in some rows, nulls will be substituted. }}}
rows : Required
marshalled_rows : For row : rows Select sql_marshall_list(args : row, provider : provider)
column_types :
For row : marshalled_rows
Reduce
For r : row, a : acc, n : Name
Select n : If a Is Null || r Is Null Then r?.sql_type ?? a Else synthetic_sql_type { coerce : a.coerce B& r.sql_type.coerce }
With acc : {}
table_name : Id
known_columns :
For type : column_types, name : Name
Select name :
If type.coerce == 0
Then Error "Column “\(name)” in literal table does not have consistent type."
Else column_tmpl { sql_expr : provider.symbol_start Enforce Str & table_name Enforce Str & provider.symbol_end Enforce Str & "." & provider.symbol_start Enforce Str & name & provider.symbol_end Enforce Str sql_type : type }
column_tmpl : Template {
sql_tables : Now utils_lib.frame(sql_from, name : Id)
sql_non_aggregate_columns : utils_lib.frame(sql_expr, name : Id)
sql_contains_aggregate : False
}
sql_from : (provider.generate_literal_from {}).value
}
sql_arith_ops : {
add : { id : 1 symbol : "+" }
sub : { id : 2 symbol : "-" }
mul : { id : 4 symbol : "*" }
div : { id : 8 symbol : "/" }
mod : { id : 16 symbol : "%" }
}
{{{All the rules for arithemtic operations in SQL.}}}
sql_aritmetic_tmpl : Template {
ii : {
ops : sql_arith_ops.add.id B| sql_arith_ops.sub.id B| sql_arith_ops.mul.id B| sql_arith_ops.div.id B| sql_arith_ops.mod.id
left_type : sql_types.int.id
right_type : sql_types.int.id
result_type : sql_types.int
render : Template {
value : "(" & specials.l.sql_expr Enforce Str & sql_op.symbol Enforce Str & specials.r.sql_expr Enforce Str & ")"
}
}
nn : {
ops : sql_arith_ops.add.id B| sql_arith_ops.sub.id B| sql_arith_ops.mul.id B| sql_arith_ops.div.id
left_type : sql_types.int.id B| sql_types.float.id
right_type : sql_types.int.id B| sql_types.float.id
result_type : sql_types.float
render : Template {
value : "(" & specials.l.sql_expr Enforce Str & sql_op.symbol Enforce Str & specials.r.sql_expr Enforce Str & ")"
}
}
tv : {
ops : sql_arith_ops.add.id B| sql_arith_ops.sub.id
left_type : sql_types.timestamp.id
right_type : sql_types.interval.id
result_type : sql_types.timestamp
render : Template {
value : "(" & specials.l.sql_expr Enforce Str & sql_op.symbol Enforce Str & specials.r.sql_expr Enforce Str & ")"
}
}
tt : {
ops : sql_arith_ops.sub.id
left_type : sql_types.timestamp.id
right_type : sql_types.timestamp.id
result_type : sql_types.interval
render : Template {
value : "(" & specials.l.sql_expr Enforce Str & sql_op.symbol Enforce Str & specials.r.sql_expr Enforce Str & ")"
}
}
vi : {
ops : sql_arith_ops.mul.id
left_type : sql_types.interval.id B| sql_types.int.id
right_type : sql_types.interval.id B| sql_types.int.id
result_type : sql_types.interval
render : Template {
value : "(" & specials.l.sql_expr Enforce Str & sql_op.symbol Enforce Str & specials.r.sql_expr Enforce Str & ")"
}
}
vv : {
ops : sql_arith_ops.add.id B| sql_arith_ops.sub.id
left_type : sql_types.interval.id
right_type : sql_types.interval.id
result_type : sql_types.interval
render : Template {
value : "(" & specials.l.sql_expr Enforce Str & sql_op.symbol Enforce Str & specials.r.sql_expr Enforce Str & ")"
}
}
}
sql_types : {
blob : {
name : "BLOB"
id : 64
cast : blob.id
coerce : blob.id
}
bool : {
name : "BOOLEAN"
id : 1
cast : bool.id B| int.id B| str.id
coerce : bool.id B| str.id
}
float : {
name : "DOUBLE"
id : 2
cast : int.id B| str.id
coerce : float.id B| str.id
}
int : {
name : "INTEGER"
id : 4
cast : bool.id B| float.id B| str.id
coerce : int.id B| float.id B| str.id
}
interval : {
name : "INTERVAL"
id : 32
cast : 0
coerce : interval.id B| str.id
}
str : {
name : "VARCHAR(4096)"
id : 8
cast : bool.id B| int.id B| float.id B| str.id
coerce : str.id
}
timestamp : {
name : "TIMESTAMP"
id : 16
cast : 0
coerce : str.id B| timestamp.id
}
any : blob.id B| bool.id B| int.id B| interval.id B| float.id B| str.id B| timestamp.id
numeric : int.id B| float.id
}
{{{A template for implementing a new SQL provider.}}}
sql_provider : Template {
{{{The expression templates available for instantiation.}}}
expr : allowed_expressions {}
{{{A transformation set for string escaping. Yes, this is not standard.}}}
transformations : Required
{{{The preamble starting a string.}}}
quote_start : "'"
{{{The postamble end a string.}}}
quote_end : "'"
{{{The preamble of a symbol (table, schema, or column name).}}}
symbol_start : "\""
{{{The postamble of a symbol (table, schema, or column name).}}}
symbol_end : "\""
{{{The preamble of a blob.}}}
blob_start : "X'"
{{{The postamble of a blob.}}}
blob_end : "'"
{{{The type of a blob.}}}
blob_type : "BLOB"
{{{The \Mono{FROM} clause to use for a query with no tables.}}}
null_from : ""
{{{An SQL statement that returns tables and views as a frame of frames with a
\{sql_from} containing the string needed to include this entity in a
\Mono{FROM} clause. Other implementation-specific attributes are permitted.}}}
table_discovery : Template retrieve {
sql_row_tmpl : Template {
known_columns : column_discovery(connection : connection, table : This)
}
}
{{{An SQL statement that will return the columns for a particular table or
view as a frame of frames extending \{expr.base_expr_tmpl}. Other
implementation-specific attributes are permitted.}}}
column_discovery : Template retrieve {
table : Required
sql_row_tmpl +: {
sql_tables : Now utils_lib.frame(table.sql_from, name : GenerateId table)
}
}
{{{List of arithmetic rules this database supports.}}}
arithmetic_rules : sql_aritmetic_tmpl {}
{{{Generate a FROM clause containing literal data.}}}
generate_literal_from : Template {
column_list :
(For name : Name, type : column_types, pos : Ordinal
Reduce acc & (If pos > 1 Then "," Else "") & symbol_start Enforce Str & name & symbol_end Enforce Str
With acc : symbol_start Enforce Str & table_name Enforce Str & symbol_end Enforce Str & "(") & ")"
value_list :
For row : marshalled_rows, pos : Ordinal
Reduce
acc &
(If pos > 1 Then "," Else "") &
(For column : row, type : column_types, name : Name, pos : Ordinal
Reduce acc & (If pos > 1 Then "," Else "") & (column?.sql_expr ?? "NULL")
With acc : "(") & ")"
With acc : ""
value : "(VALUES " & value_list Enforce Str & ") AS " & column_list
}
{{{The kinds of expressions that maybe used in an SQL query.}}}
allowed_expressions : Template {
{{{A base template representing an expression or column in a SQL query.}}}
base_expr_tmpl : Template {
{{{A string containing the SQL code needed to access this column.}}}
sql_expr : Required
{{{A member of \{sql_types} that represents the Flabbergast type to which
this expression will be converted.}}}
sql_type : Required
{{{All the tables required by this expression.}}}
sql_tables : Required
{{{Any columns that are not arguments to an aggregate function.}}}
sql_non_aggregate_columns : Required
{{{Whether this expression contains an aggregate function.}}}
sql_contains_aggregate : Required
}
base_const_tmpl : Template base_expr_tmpl {
sql_tables : {}
sql_non_aggregate_columns : {}
sql_contains_aggregate : False
}
base_unary_expr_tmpl : Template base_expr_tmpl {
{{{The argument to cast.}}}
arg : Required
sql_tables : arg.sql_tables
sql_non_aggregate_columns : arg.sql_non_aggregate_columns
sql_contains_aggregate : arg.sql_contains_aggregate
}
base_unary_function_tmpl : Template base_unary_expr_tmpl {
sql_arg_type : Required
sql_func : Required
sql_expr : If sql_arg_type.id B& arg.sql_type.coerce == 0
Then Error "Cannot convert \(arg.sql_type.name) to \(sql_arg_type.name)."
Else sql_func Enforce Str & "(" & arg.sql_expr Enforce Str & ")"
}
base_binary_expr_tmpl : Template base_expr_tmpl {
left : Required
right : Required
specials : sql_marshall_list(args : { l : left r : right }, provider : provider)
sql_tables : For left_table : specials.l.sql_tables, right_table : specials.r.sql_tables, name : Name Select name : left_table ?? right_table
sql_non_aggregate_columns : For left_cols : specials.l.sql_non_aggregate_columns, right_cols : specials.r.sql_non_aggregate_columns, name : Name Select name : left_cols ?? right_cols
sql_contains_aggregate : specials.l.sql_contains_aggregate || specials.r.sql_contains_aggregate
}
base_arith_expr_tmpl : Template base_binary_expr_tmpl {
sql_rule :
For arith : provider.arithmetic_rules
Where
arith.ops B& sql_op.id != 0 &&
specials.l.sql_type.coerce B& arith.left_type != 0 &&
specials.r.sql_type.coerce B& arith.right_type != 0
Reduce acc ?? arith
With acc : Null
sql_type :
If sql_rule Is Null
Then sql_types.int # The type doesn't matter. We're just trying to avoid absurd errors.
Else sql_rule.result_type
sql_expr :
If sql_rule Is Null
Then Error "Cannot find arithmetic for \(specials.l.sql_type.name) \(sql_op.symbol) \(specials.r.sql_type.name)."
Else (sql_rule.render{}).value
}
base_args_tmpl : Template base_expr_tmpl {
sql_args : sql_marshall_list(args : args, provider : provider)
sql_tables :
For arg : sql_args
Reduce For table : arg.sql_tables, acc_table : acc, name : Name Select name : table ?? acc_table
With acc : {}
}
base_infix_expr_tmpl : Template base_args_tmpl {
{{{The expressions to be put together.}}}
args : Required
{{{The infix operation symbol.}}}
sql_op : Required
{{{The bits for the allowed types of arguments.}}}
sql_allowed_arg_type_id : Required
sql_expr :
If sql_type.coerce == 0 Then Error "Cannot compute type." Else
(For arg : sql_args, pos : Ordinal
Reduce acc & (If pos > 1 Then sql_op Else "") & arg.sql_expr
With acc : "(") & ")"
sql_type : synthetic_sql_type {
coerce :
For arg : sql_args, pos : Ordinal
Reduce
If acc B& arg.sql_type.coerce == 0
Then Error "Argument \(pos) to \(sql_op) has incompatible type \(arg.sql_type.name)."
Else acc B& arg.sql_type.coerce
With acc : sql_allowed_arg_type_id
}
sql_non_aggregate_columns :
For arg : sql_args
Reduce For acc_cols : acc, arg_cols : arg.sql_non_aggregate_columns, name : Name Select name : acc_cols ?? arg_cols
With acc : {}
sql_contains_aggregate : For arg : sql_args Reduce acc || arg.sql_contains_aggregate With acc : False
}
base_function_expr_tmpl : Template base_args_tmpl {
{{{The expressions to be put together.}}}
args : Required
{{{The function name.}}}
sql_func : Required
{{{A list of argument types.}}}
arg_types : Required
sql_expr :
(For arg : sql_args, arg_type : arg_types, pos : Ordinal
Reduce
If arg_type.id B& arg.sql_type.coerce == 0
Then Error "Incompatible type \(arg.sql_type.name) for argument \(pos) to \(sql_func). Expected \(arg_type.name)."
Else acc & (If pos > 1 Then "," Else "") & arg.sql_expr
With acc : sql_func & "(") & ")"
sql_non_aggregate_columns :
For arg : sql_args
Reduce For acc_cols : acc, arg_cols : arg.sql_non_aggregate_columns, name : Name Select name : acc_cols ?? arg_cols
With acc : {}
sql_contains_aggregate : For arg : sql_args Reduce acc || arg.sql_contains_aggregate With acc : False
}
base_aggregate_func_tmpl : Template base_expr_tmpl {
{{{The expression aggregate.}}}
arg : Required
{{{The function name.}}}
sql_func : Required
{{{The bits for the allowed types of arguments.}}}
sql_allowed_arg_type_mask : Required
{{{Extra arguments, if required.}}}
sql_extra_args : ""
sql_aggregate_context : sql_func & " function arguments"
parent_sql_aggregate_context : Lookup sql_aggregate_context In Container
sql_expr :
If !(parent_sql_aggregate_context Is Null)
Then Error "Aggregate function \(sql_func) not allowed in \(parent_sql_aggregate_context)."
Else If arg.sql_type.coerce B& sql_allowed_arg_type_mask == 0
Then Error "Bad type in \(sql_func)."
Else sql_func & "(" & arg.sql_expr & sql_extra_args & ")"
sql_tables : arg.sql_tables
sql_type : synthetic_sql_type {
coerce : arg.sql_type.coerce B& sql_allowed_arg_type_mask
}
sql_non_aggregate_columns : {}
sql_contains_aggregate : True
}
base_variadic_func_tmpl : Template base_args_tmpl {
{{{The expressions aggregate.}}}
args : Required
{{{The infix operation symbol.}}}
sql_func : Required
{{{The bits for the allowed types of arguments.}}}
sql_allowed_arg_type_id : Required
sql_expr :
If sql_type.coerce == 0 Then Error "Bad type in \(sql_func)." Else
(For arg : sql_args, pos : Ordinal
Reduce acc & (If pos > 1 Then ", " Else "") & arg.sql_expr
With acc : sql_func & "(") & ")"
sql_type : synthetic_sql_type {
coerce :
For arg : sql_args, pos : Ordinal
Reduce
If acc B& arg.sql_type.coerce == 0
Then Error "Argument \(pos) to \(sql_func) has incompatible type \(arg.sql_type.name)."
Else acc B& arg.sql_type.coerce
With acc : sql_allowed_arg_type_id
}
sql_non_aggregate_columns :
For arg : sql_args
Reduce For acc_cols : acc, arg_cols : arg.sql_non_aggregate_columns, name : Name Select name : acc_cols ?? arg_cols
With acc : {}
sql_contains_aggregate : For arg : sql_args Reduce acc || arg.sql_contains_aggregate With acc : False
}
base_compare_op_tmpl : Template base_binary_expr_tmpl {
{{{The comparison symbol.}}}
sql_op : Required
type_mask : sql_types.any
sql_type : sql_types.bool
sql_expr :
If specials.l.sql_type.coerce B& specials.r.sql_type.coerce B& type_mask == 0
Then Error "Incompatible types in comparison."
Else "(\(specials.l.sql_expr) \(sql_op) \(specials.r.sql_expr))"
}
add : Template base_arith_expr_tmpl {
sql_op : sql_arith_ops.add
}
and : Template base_infix_expr_tmpl {
sql_op : "AND"
sql_allowed_arg_type_id : sql_types.bool.id
}
avg : Template base_aggregate_func_tmpl {
sql_func : "AVG"
sql_allowed_arg_type_mask : sql_types.numeric
}
base_case_tmpl : Template base_expr_tmpl {
cases : Required
else : Null
when : Template {
test : Required
result : Required
when_specials : sql_marshall_list(args : { t : test r : result }, provider : provider)
sql_type : when_specials.r.sql_type
sql_when :
If when_specials.t.sql_type.coerce B& sql_allowed_test_type.coerce == 0
Then Error "Incompatible type \(when_specials.t.sql_type.name) used in test for WHEN. Expected \(sql_allowed_test_type.name)."
Else " WHEN \(when_specials.t.sql_expr) THEN \(when_specials.r.sql_expr)"
sql_tables : For test_table : when_specials.t.sql_tables, input_table : when_specials.r.sql_tables, name : Name Select name : test_table ?? input_table
sql_non_aggregate_columns : For test_cols : when_specials.t.sql_non_aggregate_columns, result_cols : when_specials.r.sql_non_aggregate_columns, name : Name Select name : test_cols ?? result_cols
sql_contains_aggregate : when_specials.t.sql_contains_aggregate || when_specials.r.sql_contains_aggregate
}
specials : Template {
e : else
}
sql_specials : sql_marshall_list(args : specials {}, provider : provider)
sql_expr :
(For case : cases
Reduce acc & case.sql_when
With acc : "(CASE " & input_expr) &
(If sql_specials.e Is Null Then "" Else " ELSE " & sql_specials.e.sql_expr) &
" END)"
sql_type : synthetic_sql_type {
coerce :
For case : cases, pos : Ordinal
Reduce
If acc B& case.sql_type.coerce == 0
Then Error "WHEN block \(pos) has incompatible type \(case.sql_type.name)."
Else acc B& case.sql_type.coerce
With acc : If sql_specials.e Is Null Then sql_types.any Else sql_specials.e.sql_type.coerce
}
sql_tables : For s : sql_specials
Where !(s Is Null)
Reduce For table : s.sql_tables, acc_table : acc, name : Name Select name : table ?? acc_table
With acc : (For c : cases
Reduce For table : c.sql_tables, acc_table : acc, name : Name Select name : table ?? acc_table
With acc : {})
sql_non_aggregate_columns : For s : sql_specials
Where !(s Is Null)
Reduce For acc_cols : acc, special_cols : s.sql_non_aggregate_columns, name : Name Select name : acc_cols ?? special_cols
With acc : (For c : cases
Reduce For acc_cols : acc, case_cols : c.sql_non_aggregate_columns, name : Name Select name : acc_cols ?? case_cols
With acc : {})
sql_contains_aggregate : For s : sql_specials
Where !(s Is Null)
Reduce acc || s.sql_contains_aggregate
With acc : (For c : cases
Reduce acc || c.sql_contains_aggregate
With acc : False)
}
case_bool : Template base_case_tmpl {
sql_allowed_test_type : sql_types.bool
input_expr : ""
}
case_input : Template base_case_tmpl {
input : Required
specials +: { i : input }
input_expr : sql_specials.i.sql_expr
sql_allowed_test_type : sql_specials.i.sql_type
}
cast : Template base_unary_expr_tmpl {
{{{The type to change it to.}}}
sql_type : Required
sql_expr : If sql_type.id B& arg.sql_type.cast == 0
Then Error "Cannot perform cast with provided types."
Else "(CAST(\(arg.sql_expr Enforce Str) AS \(If sql_type.id Enforce Int == sql_types.blob.id Then provider.blob_type Enforce Str Else sql_type.name Enforce Str)))"
}
count : Template base_aggregate_func_tmpl {
sql_func : "COUNT"
sql_allowed_arg_type_mask : sql_types.any
sql_type : sql_types.int
}
div : Template base_arith_expr_tmpl {
sql_op : sql_arith_ops.div
}
equal : Template base_compare_op_tmpl {
sql_op : "=="
}
extract : Template base_unary_expr_tmpl {
field : Required
year : "YEAR"
month : "MONTH"
day : "DAY"
hour : "HOUR"
minute : "MINUTE"
second : "SECOND"
sql_start : "EXTRACT("
sql_middle : " FROM "
sql_end : ")"
sql_type : sql_types.int
sql_expr :
If arg.sql_type.coerce B& sql_types.timestamp.id == 0
Then Error "Cannot extract on non-timestamp \(arg.sql_type.name)."
Else sql_start Enforce Str & field Enforce Str & sql_middle Enforce Str & arg.sql_expr Enforce Str & sql_end Enforce Str
}
ge : Template base_compare_op_tmpl {
sql_op : ">="
}
gt : Template base_compare_op_tmpl {
sql_op : ">"
}
greatest : Template base_variadic_func_tmpl {
sql_func : "GREATEST"
sql_allowed_arg_type_id : sql_types.numeric
}
interval_const : Template base_const_tmpl {
{{{The number of seconds in this interval.}}}
time : Required
sql_type : sql_types.interval
}
is_null : Template base_unary_expr_tmpl {
sql_type : sql_types.bool
sql_expr :
If sql_type.coerce == 0 Then Error "Bad type in null check."
Else "(\(arg.sql_expr) IS NULL)"
}
le : Template base_compare_op_tmpl {
sql_op : "<="
}
least : Template base_variadic_func_tmpl {
sql_func : "LEAST"
sql_allowed_arg_type_id : sql_types.numeric
}
like : Template base_binary_expr_tmpl {
{{{Whether the matching should be case-sensitive.}}}
case_sensitive : True
sql_type : sql_types.bool
sql_expr :
If specials.l.sql_type.coerce B& specials.r.sql_type.coerce B& sql_types.str.id == 0
Then Error "Incompatible types in comparison."
Else If case_sensitive
Then "(\(specials.l.sql_expr) LIKE \(specials.r.sql_expr))"
Else "(UPPER(\(specials.l.sql_expr)) LIKE UPPER(\(specials.r.sql_expr)))"
}
lower : Template base_unary_function_tmpl {
sql_arg_type : sql_types.str
sql_type : sql_types.str
sql_func : "LOWER"
}
lt : Template base_compare_op_tmpl {
sql_op : "<"
}
max : Template base_aggregate_func_tmpl {
sql_func : "MAX"
sql_allowed_arg_type_mask : sql_types.numeric
}
min : Template base_aggregate_func_tmpl {
sql_func : "MIN"
sql_allowed_arg_type_mask : sql_types.numeric
}
mod : Template base_arith_expr_tmpl {
sql_op : sql_arith_ops.mod
}
mul : Template base_arith_expr_tmpl {
sql_op : sql_arith_ops.mul
}
negate : Template base_unary_expr_tmpl {
sql_type : arg.sql_type
sql_expr :
If arg.sql_type.coerce B& sql_types.numeric != 0
Then "(- \(arg.sql_expr))"
Else Error "Argument to negation is not numeric."
}
not : Template base_unary_expr_tmpl {
sql_type : sql_types.bool
sql_expr :
If arg.sql_type.coerce B& sql_types.bool.id != 0
Then "(NOT \(arg.sql_expr))"
Else Error "Argument to NOT expression is not Boolean."
}
now : Template base_const_tmpl {
{{{Use UTC rather than local time.}}}
utc : True
sql_type : sql_types.timestamp
}
or : Template base_infix_expr_tmpl {
sql_op : "OR"
sql_allowed_arg_type_id : sql_types.bool.id
}
str_agg : Template base_aggregate_func_tmpl {
{{{The separator expression.}}}
separator : ""
sql_allowed_arg_type_mask : sql_types.str.id
specials : sql_marshall_list(args : { s : separator }, provider : provider)
sql_tables : For a : arg.sql_tables, s : specials.s.sql_tables, n : Name Select n : a ?? s
}
str_join : Template base_infix_expr_tmpl {
sql_op : "||"
sql_allowed_arg_type_id : sql_types.str.id
}
str_slice : Template base_expr_tmpl {
input : Required
start : Required
length : Required
sql_substr_syntax : "SUBSTRING(" & specials.i.sql_expr & " FROM " & specials.s.sql_expr & " FOR " & specials.l.sql_expr & ")"
specials : sql_marshall_list(args : { i : input s : start l : length }, provider : provider)
sql_type : sql_types.str
sql_expr :
If specials.i.sql_type.coerce B& sql_types.str.id == 0 Then Error "Input of string slice is not a string."
Else If specials.s.sql_type.coerce B& sql_types.int.id == 0 Then Error "Start of string slice is not an integer."
Else If specials.l.sql_type.coerce B& sql_types.int.id == 0 Then Error "Length of string slice is not an integer."
Else sql_substr_syntax
sql_tables : For input_table : specials.i.sql_tables, start_table : specials.s.sql_tables, length_table : specials.l.sql_tables, name : Name Select name : (input_table ?? start_table) ?? right_table
sql_non_aggregate_columns : For s : specials
Reduce For acc_cols : acc, special_cols : s.sql_non_aggregate_columns, name : Name Select name : acc_cols ?? special_cols
With acc : {}
sql_contains_aggregate : For s : specials
Reduce acc || s.sql_contains_aggregate
With acc : False
}
sub : Template base_arith_expr_tmpl {
sql_op : sql_arith_ops.sub
}
sum : Template base_aggregate_func_tmpl {
sql_func : "SUM"
sql_allowed_arg_type_mask : sql_types.numeric
}
unequal : Template base_compare_op_tmpl {
sql_op : "<>"
}
upper : Template base_unary_function_tmpl {
sql_arg_type : sql_types.str
sql_type : sql_types.str
sql_func : "UPPER"
}
}
}
sql_provider_with_union_literal : Template sql_provider {
generate_literal_from : Template {
value_list :
For row : marshalled_rows, pos : Ordinal
Reduce
acc &
(If pos > 1 Then " UNION " Else "") &
(For column : row, type : column_types, name : Name, pos : Ordinal
Reduce acc & (If pos > 1 Then "," Else "") & (column?.sql_expr ?? "NULL") & " AS " & symbol_start Enforce Str & name & symbol_end Enforce Str
With acc : "SELECT ") & provider.null_from Enforce Str
With acc : ""
value : "(" & value_list Enforce Str & ") AS " & symbol_start Enforce Str & table_name Enforce Str & symbol_end Enforce Str
}
}
sql_providers : {
mssql : Template sql_provider {
table_discovery +: {
sql_query : "SELECT QUOTENAME(table_schema) + '.' + QUOTENAME(table_name) AS sql_from, QUOTENAME(table_schema, '''') as table_schema, QUOTENAME(table_name, '''') AS table_name, LOWER(LEFT(table_schema, 1)) + RIGHT(table_schema, LEN(table_schema) - 1) + '_' + table_name AS [ATTRNAME] FROM information_schema.tables WHERE UPPER(table_schema) NOT IN ('SYS', 'INFORMATION_SCHEMA') AND PATINDEX('%[^a-zA-Z0-9_]%', table_schema) = 0 AND PATINDEX('%[^a-zA-Z0-9_]%', table_name) = 0;"
}
column_discovery +: {
sql_query : "SELECT QUOTENAME(table_schema) + '.' + QUOTENAME(table_name) + '.' + QUOTENAME(column_name) AS sql_expr, LOWER(LEFT(column_name, 1)) + RIGHT(column_name, LEN(column_name) - 1) AS [ATTRNAME], data_type, 'sql_types.' + data_xlate.lookup AS [$sql_type] FROM information_schema.columns, (VALUES ('bigint', 'int'), ('decimal', 'int'), ('int', 'int'), ('money', 'int'), ('numeric', 'int'), ('smallint', 'int'), ('smallmoney', 'int'), ('tinyint', 'int'), ('bit', 'bool'), ('float', 'float'), ('real', 'float'), ('%char%', 'str'), ('%text%', 'str'), ('%binary%', 'str'), ('%datetime%', 'timestamp'), ('time%', 'timestamp'), ('date', 'timestamp'), ('%binary', 'blob')) AS data_xlate (name, lookup) WHERE table_schema = \(table.table_schema) AND table_name = \(table.table_name) AND data_type LIKE data_xlate.name AND PATINDEX('%[^a-zA-Z0-9_]%', column_name) = 0;"
}
quote_start : "('"
quote_end : "')"
symbol_start : "["
symbol_end : "]"
blob_type : "VARBINARY(MAX)"
transformations : {
quote : utils_lib.str_transform.char_tmpl { char : "'" replacement : "''" }
nonprint : utils_lib.str_transform.range_tmpl { start : "\x00" end : "\x1F" replacement : [ "' + NCHAR(", utf8_0, ") + '" ] }
unicode : utils_lib.str_transform.range_tmpl { start : "\u0100" end : "\uFFFF" replacement : [ "' + NCHAR(", utf32, ") + '" ] mode : mode_decimal }
}
allowed_expressions +: {
extract +: {
year : "year"
month : "month"
day : "day"
hour : "hour"
minute : "minute"
second : "second"
sql_start : "DATEPART('"
sql_middle : "', "
sql_end : ")"
}
interval_const +: {
sql_expr : time Enforce Int
}
str_agg +: {
sql_func : Null
sql_extra_args : Error "String aggregation is not supported on Microsoft SQL Server/T-SQL."
}
str_join : Template base_variadic_func_tmpl {
sql_func : "CONCAT"
sql_allowed_arg_type_id : sql_types.str.id
}
str_slice +: {
sql_substr_syntax : "SUBSTRING(" & specials.i.sql_expr & ", " & specials.s.sql_expr & ", " & specials.l.sql_expr & ")"
}
now +: {
sql_expr : If utc Then "GETUTCDATE()" Else "CURRENT_TIMESTAMP"
}
}
arithmetic_rules : sql_aritmetic_tmpl {
tv : {
ops : sql_arith_ops.add.id B| sql_arith_ops.sub.id
left_type : sql_types.timestamp.id
right_type : sql_types.interval.id
result_type : sql_types.timestamp
render : Template {
value : "DATEADD('second', 0 " & sql_op.symbol Enforce Str & specials.r.sql_expr Enforce Str & ", " & specials.l.sql_expr Enforce Str & ")"
}
}
tt : {
ops : sql_arith_ops.sub.id
left_type : sql_types.timestamp.id
right_type : sql_types.timestamp.id
result_type : sql_types.interval
render : Template {
value : "DATEDIFF('second', " & specials.l.sql_expr Enforce Str & ", " & specials.r.sql_expr Enforce Str & ")"
}
}
}
}
mysql : Template sql_provider_with_union_literal {
table_discovery +: {
sql_query : "SELECT QUOTE(TABLE_SCHEMA) AS tbl_schema, QUOTE(TABLE_NAME) AS tbl_name, CONCAT('`', TABLE_SCHEMA, '`.`', TABLE_NAME, '`') AS sql_from, CONCAT(LOWER(SUBSTRING(TABLE_NAME, 0, 1)), SUBSTRING(TABLE_NAME, 1)) AS ATTRNAME FROM information_schema.tables WHERE TABLE_SCHEMA NOT IN ('information_schema', 'performance_schema')"
}
column_discovery +: {
sql_query : "SELECT CONCAT('`', TABLE_SCHEMA, '`.`', TABLE_NAME, '`.`', COLUMN_NAME, '`') AS sql_expr, CONCAT(LOWER(SUBSTRING(COLUMN_NAME, 1, 1)), SUBSTRING(COLUMN_NAME, 2)) AS ATTRNAME, lookup AS `$sql_type` FROM information_schema.COLUMNS, (SELECT 'bit%' AS type_pattern, 'sql_types.bool' AS lookup UNION SELECT '%int%' AS type_pattern, 'sql_types.int' AS lookup UNION SELECT 'float%' AS type_pattern, 'sql_types.float' AS lookup UNION SELECT 'double%' AS type_pattern, 'sql_types.float' AS lookup UNION SELECT 'decimal%' AS type_pattern, 'sql_types.float' AS lookup UNION SELECT '%char%' AS type_pattern, 'sql_types.str' AS lookup UNION SELECT '%blob%' AS type_pattern, 'sql_types.str' AS lookup UNION SELECT 'enum%' AS type_pattern, 'sql_types.str' AS lookup UNION SELECT 'text' AS type_pattern, 'sql_types.str' AS lookup UNION SELECT 'date' AS type_pattern, 'sql_types.timestamp' AS lookup UNION SELECT 'time%' AS type_pattern, 'sql_types.timestamp' AS lookup UNION SELECT 'year%' AS type_pattern, 'sql_types.timestamp' AS lookup UNION SELECT 'interval%' AS type_pattern, 'sql_types.interval' AS lookup UNION SELECT 'varbinary%' AS type_pattern, 'sql_types.blob' AS lookup UNION SELECT 'blob%' AS type_pattern, 'sql_types.blob' AS lookup) AS type_info WHERE COLUMN_TYPE LIKE type_pattern AND TABLE_SCHEMA=\(table.tbl_schema) AND TABLE_NAME=\(table.tbl_name)"
}
transformations : {
backslash : utils_lib.str_transform.char_tmpl { char : "\\" replacement : "\\\\" }
backspace : utils_lib.str_transform.char_tmpl { char : "\b" replacement : "\\b" }
newline : utils_lib.str_transform.char_tmpl { char : "\n" replacement : "\\n" }
null : utils_lib.str_transform.char_tmpl { char : "\x00" replacement : "\\0" }
return : utils_lib.str_transform.char_tmpl { char : "\r" replacement : "\\r" }
quote : utils_lib.str_transform.char_tmpl { char : "'" replacement : "\\'" }
double_quote : utils_lib.str_transform.char_tmpl { char : "\"" replacement : "\\\"" }
tab : utils_lib.str_transform.char_tmpl { char : "\t" replacement : "\\t" }
eof : utils_lib.str_transform.char_tmpl { char : "\x1A" replacement : "\\Z" }
}
symbol_start : "`"
symbol_end : "`"
allowed_expressions +: {
interval_const +: {
sql_expr : "INTERVAL \(time Enforce Int) SECONDS"
}
now +: {
sql_expr : If utc Then "UTC_TIMESTAMP()" Else "NOW()"
}
str_agg +: {
sql_func : "GROUP_CONCAT"
sql_extra_args : " SEPARATOR " & specials.s.sql_expr
}
str_join : Template base_variadic_func_tmpl {
sql_func : "CONCAT"
sql_allowed_arg_type_id : sql_types.str.id
}
}
}
oracle : Template sql_provider_with_union_literal {
table_discovery +: {
sql_query : "SELECT '\"' || table_name || '\"' AS \"sql_from\", table_name AS \"table_name\", REGEXP_REPLACE(REGEXP_REPLACE(LOWER(table_name), '[^a-zA-Z0-9_]', '_'), '^([^a-z])', 't\\1') AS \"ATTRNAME\" FROM user_tables"
}
column_discovery +: {
sql_query : "SELECT '\"' || table_name || '\".\"' || column_name || '\"' AS \"sql_expr\", REGEXP_REPLACE(REGEXP_REPLACE(LOWER(column_name), '[^a-zA-Z0-9_]', '_'), '^([^a-z])', 'c\\1') AS \"ATTRNAME\", data_type, 'sql_types.' || data_xlate.lookup AS \"\" FROM USER_TAB_COLUMNS , (SELECT objectschema name, objectname lookup FROM TABLE(sys.ODCIObjectList(sys.odciobject('NUMBER%', 'float'), sys.odciobject('%CHAR%', 'str'), sys.odciobject('BINARY_FLOAT', 'float'), sys.odciobject('BINARY_DOUBLE', 'float'), sys.odciobject('DATE%', 'timestamp'), sys.odciobject('TIMESTAMP%', 'timestamp'), sys.odciobject('INTERVAL%', 'interval'), sys.odciobject('%LOB', 'blob'), sys.odciobject('BFILE', 'blob')))) data_xlate WHERE table_name = '\(table_name)' AND data_type LIKE data_xlate.name"
}
transformations : {
quote : utils_lib.str_transform.char_tmpl { char : "'" replacement : "''" }
}
null_from : " FROM DUAL"
allowed_expressions +: {
interval_const +: {
sql_expr : "INTERVAL '\(time Enforce Int)' SECOND"
}
now +: {
sql_expr : If utc Then "SYS_EXTRACT_UTC(CURRENT_TIMESTAMP)" Else "CURRENT_TIMESTAMP"
}
str_agg +: {
sql_func : "LISTAGG"
sql_extra_args : ", " & specials.s.sql_expr & ") WITHIN GROUP (ORDER BY " & arg.sql_expr
}
str_slice +: {
sql_substr_syntax : "SUBSTR(" & specials.i.sql_expr & ", " & specials.s.sql_expr & ", " & specials.l.sql_expr & ")"
}
}
generate_literal_from +: {
value : "(" & value_list Enforce Str & ") " & symbol_start Enforce Str & table_name Enforce Str & symbol_end Enforce Str
}
}
postgresql : Template sql_provider {
table_discovery +: {
sql_query : "SELECT QUOTE_IDENT(table_schema) || '.' || QUOTE_IDENT(table_name) AS sql_from, QUOTE_LITERAL(table_schema) AS table_schema, QUOTE_LITERAL(table_name) AS table_name, regexp_replace(regexp_replace(table_schema || '_' || table_name, '[^a-zA-Z0-9_]', '_'), '^([^a-z])', 't\\1') AS \"ATTRNAME\" FROM information_schema.tables WHERE table_schema NOT IN ('pg_catalog', 'information_schema');"
}
column_discovery +: {
sql_query : "SELECT QUOTE_IDENT(table_schema) || '.' || QUOTE_IDENT(table_name) || '.' || QUOTE_IDENT(column_name) AS sql_expr, regexp_replace(regexp_replace(column_name, '[^a-zA-Z0-9_]', '_'), '^([^a-z])', 'c\\1') AS \"ATTRNAME\", data_type, 'sql_types.' || data_xlate.lookup AS \"$sql_type\" FROM information_schema.columns, (VALUES ('bigint', 'int'), ('bigserial', 'int'), ('integer', 'int'), ('interval%', 'int'), ('serial', 'int'), ('smallint', 'int'), ('boolean', 'bool'), ('numeric%', 'float'), ('double precision', 'float'), ('real', 'float'), ('bytea', 'str'), ('character%', 'bool'), ('text', 'str'), ('date', 'timestamp'), ('time%', 'timestamp'), ('interval%', 'interval'), ('bytea', 'blob') AS data_xlate (name, lookup) WHERE table_schema = \(table.table_schema) AND table_name = \(table.table_name) AND data_type LIKE data_xlate.name"
}
quote_start : "E'"
blob_start : "E'\\x"
blob_type : "bytea"
transformations : {
backslash : utils_lib.str_transform.char_tmpl { char : "\\" replacement : "\\\\" }
backspace : utils_lib.str_transform.char_tmpl { char : "\b" replacement : "\\b" }
formfeed : utils_lib.str_transform.char_tmpl { char : "\f" replacement : "\\f" }
newline : utils_lib.str_transform.char_tmpl { char : "\n" replacement : "\\n" }
null : utils_lib.str_transform.char_tmpl { char : "\x00" replacement : "\\0" }
quote : utils_lib.str_transform.char_tmpl { char : "'" replacement : "\\'" }
tab : utils_lib.str_transform.char_tmpl { char : "\t" replacement : "\\t" }
nonprint : utils_lib.str_transform.range_tmpl { start : "\x00" end : "\x1F" replacement : [ "\\x", utf8_0 ] }
unicode : utils_lib.str_transform.range_tmpl { start : "\u0100" end : "\uFFFF" replacement : [ "\\U", utf32] mode : mode_hex_upper }
}
allowed_expressions +: {
interval_const +: {
sql_expr : "INTERVAL '\(time Enforce Int)' SECONDS"
}
now +: {
sql_expr : "(NOW() \(If utc Then "AT TIME ZONE 'utc'" Else ""))"
}
str_agg +: {
sql_func : "STRING_AGG"
sql_extra_args : ", " & specials.s.sql_expr
}
}
}
sqlite : Template sql_provider_with_union_literal {
table_discovery +: {
sql_query : "SELECT name AS \"ATTRNAME\", tbl_name AS sql_from FROM sqlite_master WHERE type = 'table'"
}
column_discovery +: {
sql_query : "PRAGMA table_info(\"\(table.sql_from)\")"
# SQLite doesn't give us the power to control the format of the data we
# get back, so we get to do it locally. Fun.
value +orig:
For col : orig
Select col.name : sql_row_tmpl {
sql_expr : "\(table.sql_from).\(col.name)"
sql_type :
If col.type Is Null Then sql_types.str
Else If
col.type == "BOOLEAN" Then sql_types.bool
Else If
col.type == "INT" ||
col.type == "INTEGER" ||
col.type == "TINYINT" ||
col.type == "SMALLINT" ||
col.type == "MEDIUMINT" ||
col.type == "BIGINT" ||
col.type == "UNSIGNED BIG INT" ||
col.type == "INT2" ||
col.type == "INT8" Then sql_types.int
Else If
col.type == "REAL" ||
col.type == "DOUBLE" ||
col.type == "DOUBLE PRECISION" ||
col.type == "FLOAT" Then sql_types.float
Else If
col.type == "BLOB" Then sql_types.blob
Else sql_types.str
}
}
transformations : {
# This is kind of scary, but what's actually listed in the SQL92 spec and
# the SQLite tokenizer. It's unclear how it will deal with UTF-8
# surrogate pairs.
quote : utils_lib.str_transform.char_tmpl { char : "'" replacement : "''" }
}
allowed_expressions +: {
interval_const +: {
sql_expr : time Enforce Int
}
extract +: {
year : "Y"
month : "m"
day : "d"
hour : "H"
minute : "M"
second : "s"
sql_start : "CAST(STRFTIME('%"
sql_middle : "', "
sql_end : ") AS INTEGER)"
}
now +: {
sql_expr : "DATETIME('now', '\(If utc Then "utc" Else "localtime")')"
}
str_agg +: {
sql_func : "GROUP_CONCAT"
sql_extra_args : ", " & specials.s.sql_expr
}
str_slice +: {
sql_substr_syntax : "SUBSTR(" & specials.i.sql_expr & ", " & specials.s.sql_expr & ", " & specials.l.sql_expr & ")"
}
}
arithmetic_rules : sql_aritmetic_tmpl {
tt : {
ops : sql_arith_ops.sub.id
left_type : sql_types.timestamp.id
right_type : sql_types.timestamp.id
result_type : sql_types.interval
render : Template {
value : "(STRFTIME('%s', " & specials.l.sql_expr Enforce Str & ") - STRFTIME('%s', " & specials.r.sql_expr Enforce Str & "))"
}
}
tv : {
ops : sql_arith_ops.add.id B| sql_arith_ops.sub.id
left_type : sql_types.timestamp.id
right_type : sql_types.interval.id
result_type : sql_types.timestamp
render : Template {
value : "DATETIME(" & specials.l.sql_expr & ", '" & sql_op.symbol Enforce Str & "' || " & specials.r.sql_expr Enforce Str & " || ' seconds')"
}
}
}
}
}
{{{The default row template is empty, providing only the columns in the database.}}}
sql_row_tmpl : Template {}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy