// The base Parser's FUNCTIONS and *_PARSERS tables, plus the module-level builder
// helpers of sqlglot/parser.py (build_like, build_logarithm, ...), which dialects reuse.
///|
fn arg(args : Array[Expr], i : Int) -> Expr? {
args.get(i)
}
///|
fn rest(args : Array[Expr], from : Int) -> Array[Expr] {
if from >= args.length() {
[]
} else {
args[from:].to_array()
}
}
///|
pub fn build_var_map(args : Array[Expr]) -> Expr {
if args.length() == 1 && args[0].is_star() {
return mk1(StarMap, args[0])
}
let keys = []
let values = []
let mut i = 0
while i < args.length() {
keys.push(args[i])
if i + 1 < args.length() {
values.push(args[i + 1])
}
i += 2
}
mk(VarMap, [
("keys", mk(Array, [("expressions", keys)])),
("values", mk(Array, [("expressions", values)])),
])
}
///|
pub fn build_like(args : Array[Expr]) -> Expr {
let like = mk(Like, [("this", arg(args, 1)), ("expression", arg(args, 0))])
if args.length() > 2 {
mk(Escape, [("this", like), ("expression", arg(args, 2))])
} else {
like
}
}
///|
/// `binary_range_parser(expr_type, reverse_args)`
pub fn binary_range_parser(
kind : Kind,
reverse_args? : Bool = false,
) -> ParseFn1 {
fn(p, this) {
let expression = p.parse_bitwise()
let (a, b) = if reverse_args {
(expression, this)
} else {
(this, expression)
}
p.parse_escape(
Some(p.expression(mk(kind, [("this", a), ("expression", b)]))),
)
}
}
///|
pub fn build_logarithm(args : Array[Expr], p : Parser) -> Expr {
let mut this = arg(args, 0)
let mut expression = arg(args, 1)
if expression is Some(_) {
if p.dialect.cfg.log_base_first != Some(true) {
let tmp = this
this = expression
expression = tmp
}
return mk(Log, [("this", this), ("expression", expression)])
}
mk1(if p.cfg.log_defaults_to_ln { Ln } else { Log }, this)
}
///|
pub fn build_hex(args : Array[Expr], p : Parser) -> Expr {
let a = arg(args, 0)
if p.dialect.cfg.hex_lowercase {
mk1(LowerHex, a)
} else {
mk1(Hex, a)
}
}
///|
pub fn build_lower(args : Array[Expr]) -> Expr {
match arg(args, 0) {
Some(a) if a.kind.is_a(Hex) => mk1(LowerHex, a.this())
a => mk1(Lower, a)
}
}
///|
pub fn build_upper(args : Array[Expr]) -> Expr {
match arg(args, 0) {
Some(a) if a.kind.is_a(Hex) => mk1(Hex, a.this())
a => mk1(Upper, a)
}
}
///|
pub fn build_extract_json_with_path(kind : Kind) -> FuncBuilder {
fn(args, p) {
let expression = mk(kind, [
("this", arg(args, 0)),
("expression", p.dialect.to_json_path(arg(args, 1))),
])
if args.length() > 2 && kind == JSONExtract {
expression.set("expressions", rest(args, 2))
}
if kind == JSONExtractScalar {
expression.set(
"scalar_only",
p.dialect.cfg.json_extract_scalar_scalar_only,
)
}
expression
}
}
///|
pub fn build_mod(args : Array[Expr]) -> Expr {
let wrap = fn(e : Expr?) -> Expr? {
match e {
Some(x) if x.kind.is_a(Binary) => Some(mk1(Paren, x))
_ => e
}
}
mk(Mod, [("this", wrap(arg(args, 0))), ("expression", wrap(arg(args, 1)))])
}
///|
pub fn build_pad(args : Array[Expr], is_left? : Bool = true) -> Expr {
mk(Pad, [
("this", arg(args, 0)),
("expression", arg(args, 1)),
("fill_pattern", arg(args, 2)),
("is_left", is_left),
])
}
///|
pub fn build_convert_timezone(
args : Array[Expr],
default_source_tz? : String,
) -> Expr {
if args.length() == 2 {
let source_tz = match default_source_tz {
Some(s) if !s.is_empty() => Some(literal_string(s))
_ => None
}
return mk(ConvertTimezone, [
("source_tz", source_tz),
("target_tz", arg(args, 0)),
("timestamp", arg(args, 1)),
])
}
from_arg_list(ConvertTimezone, args)
}
///|
pub fn build_trim(
args : Array[Expr],
is_left? : Bool = true,
reverse_args? : Bool = false,
) -> Expr {
let mut this = arg(args, 0)
let mut expression = arg(args, 1)
if expression is Some(_) && reverse_args {
let tmp = this
this = expression
expression = tmp
}
mk(Trim, [
("this", this),
("expression", expression),
("position", if is_left { "LEADING" } else { "TRAILING" }),
])
}
///|
pub fn build_coalesce(
args : Array[Expr],
is_nvl? : Bool,
is_null? : Bool,
) -> Expr {
mk(Coalesce, [
("this", arg(args, 0)),
("expressions", rest(args, 1)),
("is_nvl", is_nvl),
("is_null", is_null),
])
}
///|
pub fn build_locate_strposition(args : Array[Expr]) -> Expr {
mk(StrPosition, [
("this", arg(args, 1)),
("substr", arg(args, 0)),
("position", arg(args, 2)),
])
}
///|
pub fn build_array_append(args : Array[Expr], p : Parser) -> Expr {
mk(ArrayAppend, [
("this", arg(args, 0)),
("expression", arg(args, 1)),
("null_propagation", p.dialect.cfg.array_funcs_propagates_nulls),
])
}
///|
pub fn build_array_prepend(args : Array[Expr], p : Parser) -> Expr {
mk(ArrayPrepend, [
("this", arg(args, 0)),
("expression", arg(args, 1)),
("null_propagation", p.dialect.cfg.array_funcs_propagates_nulls),
])
}
///|
pub fn build_array_concat(args : Array[Expr], p : Parser) -> Expr {
mk(ArrayConcat, [
("this", arg(args, 0)),
("expressions", rest(args, 1)),
("null_propagation", p.dialect.cfg.array_funcs_propagates_nulls),
])
}
///|
pub fn build_array_remove(args : Array[Expr], p : Parser) -> Expr {
mk(ArrayRemove, [
("this", arg(args, 0)),
("expression", arg(args, 1)),
("null_propagation", p.dialect.cfg.array_funcs_propagates_nulls),
])
}
///|
/// JSON operator builders shared between COLUMN_OPERATORS and JSON_OPERATORS.
pub fn build_json_extract_op(
p : Parser,
this : Expr?,
path : Expr?,
) -> Expr? raise SqlglotError {
Some(
p.expression(
mk(JSONExtract, [
("this", this),
("expression", p.dialect.to_json_path(path)),
("only_json_types", p.cfg.json_arrows_require_json_type),
]),
),
)
}
///|
pub fn build_json_extract_scalar_op(
p : Parser,
this : Expr?,
path : Expr?,
) -> Expr? raise SqlglotError {
Some(
p.expression(
mk(JSONExtractScalar, [
("this", this),
("expression", p.dialect.to_json_path(path)),
("only_json_types", p.cfg.json_arrows_require_json_type),
("scalar_only", p.dialect.cfg.json_extract_scalar_scalar_only),
]),
),
)
}
///|
pub fn build_jsonb_extract_op(
p : Parser,
this : Expr?,
path : Expr?,
) -> Expr? raise SqlglotError {
Some(p.expression(mk(JSONBExtract, [("this", this), ("expression", path)])))
}
///|
pub fn build_jsonb_extract_scalar_op(
p : Parser,
this : Expr?,
path : Expr?,
) -> Expr? raise SqlglotError {
Some(
p.expression(mk(JSONBExtractScalar, [("this", this), ("expression", path)])),
)
}
///|
pub fn build_jsonb_contains_top_key_op(
p : Parser,
this : Expr?,
key : Expr?,
) -> Expr? raise SqlglotError {
Some(
p.expression(mk(JSONBContainsTopKey, [("this", this), ("expression", key)])),
)
}
///|
fn text_cast(e : Expr?) -> Expr {
mk(Cast, [("this", e), ("to", mk1(DataType, DType::TEXT))])
}
///|
fn base_functions() -> Map[String, FuncBuilder] {
let f : Map[String, FuncBuilder] = Map([])
for nk in function_by_name {
f[nk.0] = from_arg_list_builder(nk.1)
}
for name in ["COALESCE", "IFNULL", "NVL"] {
f[name] = fn(args, _p) { build_coalesce(args) }
}
f["ARRAY"] = fn(args, _p) { mk(Array, [("expressions", args)]) }
let array_agg : FuncBuilder = fn(args, p) {
mk(ArrayAgg, [
("this", arg(args, 0)),
(
"nulls_excluded",
if p.dialect.cfg.array_agg_includes_nulls is None {
Some(true)
} else {
None
},
),
])
}
f["ARRAYAGG"] = array_agg
f["ARRAY_AGG"] = array_agg
f["ARRAY_APPEND"] = fn(args, p) { build_array_append(args, p) }
f["ARRAY_CAT"] = fn(args, p) { build_array_concat(args, p) }
f["ARRAY_CONCAT"] = fn(args, p) { build_array_concat(args, p) }
f["ARRAY_INTERSECT"] = fn(args, _p) {
mk(ArrayIntersect, [("expressions", args)])
}
f["ARRAY_INTERSECTION"] = fn(args, _p) {
mk(ArrayIntersect, [("expressions", args)])
}
f["ARRAY_PREPEND"] = fn(args, p) { build_array_prepend(args, p) }
f["ARRAY_REMOVE"] = fn(args, p) { build_array_remove(args, p) }
f["COUNT"] = fn(args, _p) {
mk(Count, [
("this", arg(args, 0)),
("expressions", rest(args, 1)),
("big_int", true),
])
}
f["CONCAT"] = fn(args, p) {
mk(Concat, [
("expressions", args),
("safe", !p.dialect.cfg.strict_string_concat),
("coalesce", p.dialect.cfg.concat_coalesce),
])
}
f["CONCAT_WS"] = fn(args, p) {
mk(ConcatWs, [
("expressions", args),
("safe", !p.dialect.cfg.strict_string_concat),
("coalesce", p.dialect.cfg.concat_ws_coalesce),
])
}
f["CONVERT_TIMEZONE"] = fn(args, _p) { build_convert_timezone(args) }
f["DATE_TO_DATE_STR"] = fn(args, _p) { text_cast(arg(args, 0)) }
f["GENERATE_DATE_ARRAY"] = fn(args, _p) {
let step = match arg(args, 2) {
Some(s) => s
None =>
mk(Interval, [("this", literal_string("1")), ("unit", var_("DAY"))])
}
mk(GenerateDateArray, [
("start", arg(args, 0)),
("end", arg(args, 1)),
("step", step),
])
}
f["GENERATE_UUID"] = fn(_args, p) {
mk(Uuid, [
(
"is_string",
if p.dialect.cfg.uuid_is_string_type {
Some(true)
} else {
None
},
),
])
}
f["GLOB"] = fn(args, _p) {
mk(Glob, [("this", arg(args, 1)), ("expression", arg(args, 0))])
}
f["GREATEST"] = fn(args, p) {
mk(Greatest, [
("this", arg(args, 0)),
("expressions", rest(args, 1)),
("ignore_nulls", p.dialect.cfg.least_greatest_ignores_nulls),
])
}
f["LEAST"] = fn(args, p) {
mk(Least, [
("this", arg(args, 0)),
("expressions", rest(args, 1)),
("ignore_nulls", p.dialect.cfg.least_greatest_ignores_nulls),
])
}
f["HEX"] = fn(args, p) { build_hex(args, p) }
f["JSON_EXTRACT"] = build_extract_json_with_path(JSONExtract)
f["JSON_EXTRACT_SCALAR"] = build_extract_json_with_path(JSONExtractScalar)
f["JSON_EXTRACT_PATH_TEXT"] = build_extract_json_with_path(JSONExtractScalar)
f["JSON_KEYS"] = fn(args, p) {
mk(JSONKeys, [
("this", arg(args, 0)),
("expression", p.dialect.to_json_path(arg(args, 1))),
])
}
f["LIKE"] = fn(args, _p) { build_like(args) }
f["LOG"] = fn(args, p) { build_logarithm(args, p) }
f["LOG2"] = fn(args, _p) {
mk(Log, [("this", literal_int(2)), ("expression", arg(args, 0))])
}
f["LOG10"] = fn(args, _p) {
mk(Log, [("this", literal_int(10)), ("expression", arg(args, 0))])
}
f["LOWER"] = fn(args, _p) { build_lower(args) }
f["LPAD"] = fn(args, _p) { build_pad(args) }
f["LEFTPAD"] = fn(args, _p) { build_pad(args) }
f["LTRIM"] = fn(args, _p) { build_trim(args) }
f["MOD"] = fn(args, _p) { build_mod(args) }
f["RIGHTPAD"] = fn(args, _p) { build_pad(args, is_left=false) }
f["RPAD"] = fn(args, _p) { build_pad(args, is_left=false) }
f["RTRIM"] = fn(args, _p) { build_trim(args, is_left=false) }
f["SCOPE_RESOLUTION"] = fn(args, _p) {
if args.length() != 2 {
mk(ScopeResolution, [("expression", arg(args, 0))])
} else {
mk(ScopeResolution, [("this", arg(args, 0)), ("expression", arg(args, 1))])
}
}
f["STRPOS"] = from_arg_list_builder(StrPosition)
f["CHARINDEX"] = fn(args, _p) { build_locate_strposition(args) }
f["INSTR"] = from_arg_list_builder(StrPosition)
f["LOCATE"] = fn(args, _p) { build_locate_strposition(args) }
f["TIME_TO_TIME_STR"] = fn(args, _p) { text_cast(arg(args, 0)) }
f["TO_HEX"] = fn(args, p) { build_hex(args, p) }
f["TS_OR_DS_TO_DATE_STR"] = fn(args, _p) {
mk(Substring, [
("this", text_cast(arg(args, 0))),
("start", literal_int(1)),
("length", literal_int(10)),
])
}
f["UNNEST"] = fn(args, _p) {
mk(Unnest, [("expressions", opt_list(arg(args, 0)))])
}
f["UPPER"] = fn(args, _p) { build_upper(args) }
f["UUID"] = fn(_args, p) {
mk(Uuid, [
(
"is_string",
if p.dialect.cfg.uuid_is_string_type {
Some(true)
} else {
None
},
),
])
}
f["UUID_STRING"] = fn(args, p) {
mk(Uuid, [
("this", arg(args, 0)),
("name", arg(args, 1)),
(
"is_string",
if p.dialect.cfg.uuid_is_string_type {
Some(true)
} else {
None
},
),
])
}
f["VAR_MAP"] = fn(args, _p) { build_var_map(args) }
f
}
///|
fn base_property_parsers() -> Map[String, PropertyFn] {
let m : Map[String, PropertyFn] = Map([])
let node = fn(e : Expr) -> Value? { Some(Node(e)) }
let opt = fn(e : Expr?) -> Value? { e.map(x => Node(x)) }
m["ALLOWED_VALUES"] = fn(p, _) {
node(
p.expression(
mk(AllowedValuesProperty, [
("expressions", p.parse_csv(() => p.parse_primary())),
]),
),
)
}
m["ALGORITHM"] = fn(p, _) {
node(p.parse_property_assignment(AlgorithmProperty))
}
m["AUTO"] = fn(p, _) { opt(p.parse_auto_property()) }
m["AUTO_INCREMENT"] = fn(p, _) {
node(p.parse_property_assignment(AutoIncrementProperty))
}
m["BACKUP"] = fn(p, _) {
node(p.expression(mk1(BackupProperty, p.parse_var(any_token=true))))
}
m["BLOCKCOMPRESSION"] = fn(p, _) { node(p.parse_blockcompression()) }
m["CALLED"] = fn(p, _) { opt(p.parse_called_on_null_input_property()) }
m["CHARSET"] = fn(p, kw) {
node(p.parse_character_set(default=kw.get("default").unwrap_or(false)))
}
m["CHECKSUM"] = fn(p, _) { node(p.parse_checksum()) }
m["CLUSTER BY"] = fn(p, _) { opt(p.parse_cluster_property()) }
m["CLUSTERED"] = fn(p, _) { node(p.parse_clustered_by()) }
m["COLLATE"] = fn(p, kw) {
let kwargs : Array[(String, &IntoValue)] = []
for k, v in kw {
kwargs.push((k, v))
}
node(p.parse_property_assignment(CollateProperty, kwargs~))
}
m["COMMENT"] = fn(p, _) {
node(p.parse_property_assignment(SchemaCommentProperty))
}
m["CONTAINS"] = fn(p, _) { opt(p.parse_contains_property()) }
m["COPY"] = fn(p, _) { opt(p.parse_copy_property()) }
m["DATABLOCKSIZE"] = fn(p, kw) {
node(
p.parse_datablocksize(
default?=kw.get("default"),
minimum?=kw.get("minimum"),
maximum?=kw.get("maximum"),
),
)
}
m["DATA_DELETION"] = fn(p, _) { node(p.parse_data_deletion_property()) }
m["DEFINER"] = fn(p, _) { opt(p.parse_definer()) }
m["DETERMINISTIC"] = fn(p, _) {
node(p.expression(mk1(StabilityProperty, literal_string("IMMUTABLE"))))
}
m["DISTRIBUTED"] = fn(p, _) { node(p.parse_distributed_property()) }
m["DUPLICATE"] = fn(p, _) {
node(p.parse_composite_key_property(DuplicateKeyProperty))
}
m["DYNAMIC"] = fn(p, _) { node(p.expression(mk0(DynamicProperty))) }
m["DISTKEY"] = fn(p, _) { node(p.parse_distkey()) }
m["DISTSTYLE"] = fn(p, _) {
node(p.parse_property_assignment(DistStyleProperty))
}
m["EMPTY"] = fn(p, _) { node(p.expression(mk0(EmptyProperty))) }
m["ENGINE"] = fn(p, _) { node(p.parse_property_assignment(EngineProperty)) }
m["ENVIRONMENT"] = fn(p, _) {
node(
p.expression(
mk(EnviromentProperty, [
("expressions", p.parse_wrapped_csv(() => p.parse_assignment())),
]),
),
)
}
m["HANDLER"] = fn(p, _) { node(p.parse_property_assignment(HandlerProperty)) }
m["EXECUTE"] = fn(p, _) {
node(p.parse_property_assignment(ExecuteAsProperty))
}
m["EXTERNAL"] = fn(p, _) { node(p.expression(mk0(ExternalProperty))) }
m["FALLBACK"] = fn(p, kw) {
node(p.parse_fallback(no=kw.get("no").unwrap_or(false)))
}
m["FORMAT"] = fn(p, _) {
node(p.parse_property_assignment(FileFormatProperty))
}
m["FREESPACE"] = fn(p, _) { node(p.parse_freespace()) }
m["GLOBAL"] = fn(p, _) { node(p.expression(mk0(GlobalProperty))) }
m["HEAP"] = fn(p, _) { node(p.expression(mk0(HeapProperty))) }
m["ICEBERG"] = fn(p, _) { node(p.expression(mk0(IcebergProperty))) }
m["IMMUTABLE"] = fn(p, _) {
node(p.expression(mk1(StabilityProperty, literal_string("IMMUTABLE"))))
}
m["INHERITS"] = fn(p, _) {
node(
p.expression(
mk(InheritsProperty, [
("expressions", p.parse_wrapped_csv(() => p.parse_table())),
]),
),
)
}
m["INPUT"] = fn(p, _) {
node(p.expression(mk1(InputModelProperty, p.parse_schema())))
}
m["JOURNAL"] = fn(p, kw) { node(p.parse_journal(kw)) }
m["LANGUAGE"] = fn(p, _) {
node(p.parse_property_assignment(LanguageProperty))
}
m["LAYOUT"] = fn(p, _) { opt(p.parse_dict_property("LAYOUT")) }
m["LIFETIME"] = fn(p, _) { opt(p.parse_dict_range("LIFETIME")) }
m["LIKE"] = fn(p, _) { opt(p.parse_create_like()) }
m["LOCATION"] = fn(p, _) {
node(p.parse_property_assignment(LocationProperty))
}
m["LOCK"] = fn(p, _) { node(p.parse_locking()) }
m["LOCKING"] = fn(p, _) { node(p.parse_locking()) }
m["LOG"] = fn(p, kw) { node(p.parse_log(no=kw.get("no").unwrap_or(false))) }
m["MATERIALIZED"] = fn(p, _) { node(p.expression(mk0(MaterializedProperty))) }
m["MERGEBLOCKRATIO"] = fn(p, kw) {
node(
p.parse_mergeblockratio(
no=kw.get("no").unwrap_or(false),
default=kw.get("default").unwrap_or(false),
),
)
}
m["MODIFIES"] = fn(p, _) { opt(p.parse_modifies_property()) }
m["MULTISET"] = fn(p, _) {
node(p.expression(mk(SetProperty, [("multi", true)])))
}
m["NO"] = fn(p, _) { opt(p.parse_no_property()) }
m["ON"] = fn(p, _) { opt(p.parse_on_property()) }
m["ORDER BY"] = fn(p, _) { opt(p.parse_order(skip_order_token=true)) }
m["OUTPUT"] = fn(p, _) {
node(p.expression(mk1(OutputModelProperty, p.parse_schema())))
}
m["PARTITION"] = fn(p, _) { opt(p.parse_partitioned_of()) }
m["PARTITION BY"] = fn(p, _) { opt(p.parse_partitioned_by()) }
m["PARTITIONED BY"] = fn(p, _) { opt(p.parse_partitioned_by()) }
m["PARTITIONED_BY"] = fn(p, _) { opt(p.parse_partitioned_by()) }
m["PRIMARY KEY"] = fn(p, _) { opt(p.parse_primary_key(in_props=true)) }
m["RANGE"] = fn(p, _) { opt(p.parse_dict_range("RANGE")) }
m["READS"] = fn(p, _) { opt(p.parse_reads_property()) }
m["REMOTE"] = fn(p, _) { node(p.parse_remote_with_connection()) }
m["RETURNS"] = fn(p, _) { opt(p.parse_returns()) }
m["STRICT"] = fn(p, _) { node(p.expression(mk0(StrictProperty))) }
m["STREAMING"] = fn(p, _) { node(p.expression(mk0(StreamingTableProperty))) }
m["ROW"] = fn(p, _) { opt(p.parse_row()) }
m["ROW_FORMAT"] = fn(p, _) {
node(p.parse_property_assignment(RowFormatProperty))
}
m["SAMPLE"] = fn(p, _) {
let this = p.andv(p.match_text("BY"), () => p.parse_bitwise())
node(p.expression(mk1(SampleProperty, this)))
}
m["SECURE"] = fn(p, _) { node(p.expression(mk0(SecureProperty))) }
m["SECURITY"] = fn(p, _) { node(p.parse_sql_security()) }
m["SQL SECURITY"] = fn(p, _) { node(p.parse_sql_security()) }
m["SET"] = fn(p, _) {
node(p.expression(mk(SetProperty, [("multi", false)])))
}
m["SETTINGS"] = fn(p, _) { node(p.parse_settings_property()) }
m["SHARING"] = fn(p, _) { node(p.parse_property_assignment(SharingProperty)) }
m["SORTKEY"] = fn(p, _) { node(p.parse_sortkey()) }
m["SOURCE"] = fn(p, _) { opt(p.parse_dict_property("SOURCE")) }
m["STABLE"] = fn(p, _) {
node(p.expression(mk1(StabilityProperty, literal_string("STABLE"))))
}
m["STORED"] = fn(p, _) { node(p.parse_stored()) }
m["SYSTEM_VERSIONING"] = fn(p, _) {
node(p.parse_system_versioning_property())
}
m["TBLPROPERTIES"] = fn(p, _) {
Some(List(p.parse_wrapped_properties().map(x => Node(x))))
}
m["TEMP"] = fn(p, _) { node(p.expression(mk0(TemporaryProperty))) }
m["TEMPORARY"] = fn(p, _) { node(p.expression(mk0(TemporaryProperty))) }
m["TO"] = fn(p, _) { opt(p.parse_to_table()) }
m["TRANSIENT"] = fn(p, _) { node(p.expression(mk0(TransientProperty))) }
m["TRANSFORM"] = fn(p, _) {
node(
p.expression(
mk(TransformModelProperty, [
("expressions", p.parse_wrapped_csv(() => p.parse_expression())),
]),
),
)
}
m["TTL"] = fn(p, _) { opt(p.parse_ttl()) }
m["USING"] = fn(p, _) {
node(p.parse_property_assignment(FileFormatProperty))
}
m["UNLOGGED"] = fn(p, _) { node(p.expression(mk0(UnloggedProperty))) }
m["VOLATILE"] = fn(p, _) { node(p.parse_volatile_property()) }
m["WITH"] = fn(p, _) { p.parse_with_property() }
m
}
///|
fn base_constraint_parsers() -> Map[String, ParseFn] {
let m : Map[String, ParseFn] = Map([])
m["AUTOINCREMENT"] = p => p.parse_auto_increment()
m["AUTO_INCREMENT"] = p => p.parse_auto_increment()
m["CASESPECIFIC"] = p => {
Some(p.expression(mk(CaseSpecificColumnConstraint, [("not_", false)])))
}
m["CHECK"] = p => p.parse_check_constraint()
m["COLLATE"] = p => {
Some(
p.expression(
mk1(
CollateColumnConstraint,
expr_or(p.parse_identifier(), () => p.parse_column()),
),
),
)
}
m["COMMENT"] = p => {
Some(p.expression(mk1(CommentColumnConstraint, p.parse_string())))
}
m["COMPRESS"] = p => p.parse_compress()
m["CLUSTERED"] = p => {
Some(
p.expression(
mk1(
ClusteredColumnConstraint,
p.parse_wrapped_csv(() => p.parse_ordered()),
),
),
)
}
m["NONCLUSTERED"] = p => {
Some(
p.expression(
mk1(
NonClusteredColumnConstraint,
p.parse_wrapped_csv(() => p.parse_ordered()),
),
),
)
}
m["DEFAULT"] = p => {
Some(p.expression(mk1(DefaultColumnConstraint, p.parse_bitwise())))
}
m["ENCODE"] = p => {
Some(p.expression(mk1(EncodeColumnConstraint, p.parse_var())))
}
m["EPHEMERAL"] = p => {
Some(p.expression(mk1(EphemeralColumnConstraint, p.parse_bitwise())))
}
m["EXCLUDE"] = p => {
Some(p.expression(mk1(ExcludeColumnConstraint, p.parse_index_params())))
}
m["FOREIGN KEY"] = p => p.parse_foreign_key()
m["FORMAT"] = p => {
Some(p.expression(mk1(DateFormatColumnConstraint, p.parse_var_or_string())))
}
m["GENERATED"] = p => p.parse_generated_as_identity()
m["IDENTITY"] = p => p.parse_auto_increment()
m["INLINE"] = p => p.parse_inline()
m["LIKE"] = p => p.parse_create_like()
m["NOT"] = p => p.parse_not_constraint()
m["NULL"] = p => {
Some(p.expression(mk(NotNullColumnConstraint, [("allow_null", true)])))
}
m["ON"] = p => {
if p.match_(UPDATE) {
Some(p.expression(mk1(OnUpdateColumnConstraint, p.parse_function())))
} else {
Some(p.expression(mk1(OnProperty, p.parse_id_var())))
}
}
m["PATH"] = p => {
Some(p.expression(mk1(PathColumnConstraint, p.parse_string())))
}
m["PERIOD"] = p => p.parse_period_for_system_time()
m["PRIMARY KEY"] = p => p.parse_primary_key()
m["REFERENCES"] = p => p.parse_references(match_=false)
m["TITLE"] = p => {
Some(p.expression(mk1(TitleColumnConstraint, p.parse_var_or_string())))
}
m["TTL"] = p => {
Some(
p.expression(
mk(MergeTreeTTL, [
("expressions", [p.parse_bitwise()].filter_map(x => x)),
]),
),
)
}
m["UNIQUE"] = p => p.parse_unique()
m["UPPERCASE"] = p => Some(p.expression(mk0(UppercaseColumnConstraint)))
m["WITH"] = p => {
Some(
p.expression(
mk(Properties, [("expressions", p.parse_wrapped_properties())]),
),
)
}
m["BUCKET"] = p => p.parse_partitioned_by_bucket_or_truncate()
m["TRUNCATE"] = p => p.parse_partitioned_by_bucket_or_truncate()
m
}
///|
pub fn Parser::parse_partitioned_by_bucket_or_truncate(
self : Parser,
) -> Expr? raise SqlglotError {
if !self.match_(L_PAREN, advance=false) {
self.retreat(self.index - 1)
return None
}
let klass = if self.prev_upper() == "BUCKET" {
PartitionedByBucket
} else {
PartitionByTruncate
}
let args = self.parse_wrapped_csv(() => {
expr_or(self.parse_primary(), () => self.parse_column())
})
let mut this = args.get(0)
let mut expression = args.get(1)
match this {
Some(t) if t.kind == Literal => {
let tmp = this
this = expression
expression = tmp
}
_ => ()
}
Some(self.expression(mk(klass, [("this", this), ("expression", expression)])))
}
///|
/// The base Parser's FUNCTIONS and *_PARSERS tables.
pub fn base_parser_fns() -> ParserFns {
let function_parsers : Map[String, ParseFn] = Map([])
for name in ArgMax.sql_names() {
function_parsers[name] = p => p.parse_distinct_arg_function(ArgMax)
}
for name in ArgMin.sql_names() {
function_parsers[name] = p => p.parse_distinct_arg_function(ArgMin)
}
function_parsers["CAST"] = p => p.parse_cast(p.cfg.strict_cast)
function_parsers["CEIL"] = p => p.parse_ceil_floor(Ceil)
function_parsers["CONVERT"] = p => p.parse_convert(p.cfg.strict_cast)
function_parsers["CHAR"] = p => p.parse_char()
function_parsers["CHR"] = p => p.parse_char()
function_parsers["DECODE"] = p => p.parse_decode()
function_parsers["EXTRACT"] = p => p.parse_extract()
function_parsers["FLOOR"] = p => p.parse_ceil_floor(Floor)
function_parsers["GAP_FILL"] = p => p.parse_gap_fill()
function_parsers["INITCAP"] = p => p.parse_initcap()
function_parsers["JSON_OBJECT"] = p => p.parse_json_object()
function_parsers["JSON_OBJECTAGG"] = p => p.parse_json_object(agg=true)
function_parsers["JSON_TABLE"] = p => p.parse_json_table()
function_parsers["MATCH"] = p => p.parse_match_against()
function_parsers["NORMALIZE"] = p => p.parse_normalize()
function_parsers["OPENJSON"] = p => p.parse_open_json()
function_parsers["OVERLAY"] = p => p.parse_overlay()
function_parsers["POSITION"] = p => p.parse_position()
function_parsers["SAFE_CAST"] = p => p.parse_cast(false, safe=true)
function_parsers["STRING_AGG"] = p => p.parse_string_agg()
function_parsers["SUBSTRING"] = p => p.parse_substring()
function_parsers["TRIM"] = p => p.parse_trim()
function_parsers["TRY_CAST"] = p => p.parse_cast(false, safe=true)
function_parsers["TRY_CONVERT"] = p => p.parse_convert(false, safe=true)
function_parsers["XMLELEMENT"] = p => p.parse_xml_element()
function_parsers["XMLTABLE"] = p => p.parse_xml_table()
let no_paren_function_parsers : Map[String, ParseFn] = {
"ANY": p => Some(p.expression(mk1(Any, p.parse_bitwise()))),
"CASE": p => p.parse_case(),
"CONNECT_BY_ROOT": p => {
Some(p.expression(mk1(ConnectByRoot, p.parse_column())))
},
"IF": p => p.parse_if(),
}
let statement_parsers : Map[TokenType, ParseFn] = Map::from_array([
(ALTER, p => p.parse_alter()),
(ANALYZE, p => p.parse_analyze()),
(BEGIN, p => p.parse_transaction()),
(CACHE, p => p.parse_cache()),
(COMMENT, p => p.parse_comment()),
(COMMIT, p => p.parse_commit_or_rollback()),
(COPY, p => p.parse_copy()),
(CREATE, p => p.parse_create()),
(DECLARE, p => p.parse_declare()),
(DELETE, p => p.parse_delete()),
(DESC, p => p.parse_describe()),
(DESCRIBE, p => p.parse_describe()),
(DROP, p => p.parse_drop()),
(GRANT, p => p.parse_grant()),
(REVOKE, p => p.parse_revoke()),
(INSERT, p => p.parse_insert()),
(KILL, p => p.parse_kill()),
(LOAD, p => p.parse_load()),
(MERGE, p => p.parse_merge()),
(PIVOT, p => p.parse_simplified_pivot()),
(PRAGMA, p => Some(p.expression(mk1(Pragma, p.parse_expression())))),
(REFRESH, p => p.parse_refresh()),
(ROLLBACK, p => p.parse_commit_or_rollback()),
(SET, p => p.parse_set()),
(TRUNCATE, p => p.parse_truncate_table()),
(UNCACHE, p => p.parse_uncache()),
(UNPIVOT, p => p.parse_simplified_pivot(is_unpivot=true)),
(UPDATE, p => p.parse_update()),
(USE, p => p.parse_use()),
(SEMICOLON, _p => Some(mk0(Semicolon))),
])
let unary_parsers : Map[TokenType, ParseFn] = Map::from_array([
(PLUS, p => p.parse_unary()),
(NOT, p => Some(p.expression(mk1(Not, p.parse_equality())))),
(TILDE, p => Some(p.expression(mk1(BitwiseNot, p.parse_unary())))),
(DASH, p => Some(p.expression(mk1(Neg, p.parse_unary())))),
(PIPE_SLASH, p => Some(p.expression(mk1(Sqrt, p.parse_unary())))),
(DPIPE_SLASH, p => Some(p.expression(mk1(Cbrt, p.parse_unary())))),
])
let string_parsers : Map[TokenType, TokenParseFn] = Map::from_array([
(
HEREDOC_STRING,
(p, t) => Some(p.expression(mk1(RawString, t.text), token=t)),
),
(
NATIONAL_STRING,
(p, t) => Some(p.expression(mk1(National, t.text), token=t)),
),
(RAW_STRING, (p, t) => Some(p.expression(mk1(RawString, t.text), token=t))),
(
STRING,
(p, t) => {
Some(
p.expression(
mk(Literal, [("this", t.text), ("is_string", true)]),
token=t,
),
)
},
),
(
UNICODE_STRING,
(p, t) => {
let escape = p.andv(p.match_text("UESCAPE"), () => p.parse_string())
Some(
p.expression(
mk(UnicodeString, [("this", t.text), ("escape", escape)]),
token=t,
),
)
},
),
])
let numeric_parsers : Map[TokenType, TokenParseFn] = Map::from_array([
(BIT_STRING, (p, t) => Some(p.expression(mk1(BitString, t.text), token=t))),
(
BYTE_STRING,
(p, t) => {
Some(
p.expression(
mk(ByteString, [
("this", t.text),
(
"is_bytes",
if p.dialect.cfg.byte_string_is_bytes_type {
Some(true)
} else {
None
},
),
]),
token=t,
),
)
},
),
(
HEX_STRING,
(p, t) => {
Some(
p.expression(
mk(HexString, [
("this", t.text),
(
"is_integer",
if p.dialect.cfg.hex_string_is_integer_type {
Some(true)
} else {
None
},
),
]),
token=t,
),
)
},
),
(
NUMBER,
(p, t) => {
Some(
p.expression(
mk(Literal, [("this", t.text), ("is_string", false)]),
token=t,
),
)
},
),
])
let primary_parsers : Map[TokenType, TokenParseFn] = Map([])
for k, v in string_parsers {
primary_parsers[k] = v
}
for k, v in numeric_parsers {
primary_parsers[k] = v
}
primary_parsers[INTRODUCER] = (p, t) => p.parse_introducer(t)
primary_parsers[NULL] = (p, _) => Some(p.expression(mk0(Null)))
primary_parsers[TRUE] = (p, _) => Some(p.expression(mk1(Boolean, true)))
primary_parsers[FALSE] = (p, _) => Some(p.expression(mk1(Boolean, false)))
primary_parsers[SESSION_PARAMETER] = (p, _) => p.parse_session_parameter()
primary_parsers[STAR] = (p, _) => p.parse_star_ops()
let placeholder_parsers : Map[TokenType, ParseFn] = Map::from_array([
(PLACEHOLDER, p => Some(p.expression(mk0(Placeholder)))),
(PARAMETER, p => p.parse_parameter()),
(
COLON,
p => {
if p.match_set(p.cfg.colon_placeholder_tokens) {
Some(p.expression(mk1(Placeholder, p.prev.text)))
} else {
None
}
},
),
])
let range_parsers : Map[TokenType, ParseFn1] = Map::from_array([
(AT_GT, binary_range_parser(ArrayContainsAll)),
(BETWEEN, (p, this) => p.parse_between(this)),
(GLOB, binary_range_parser(Glob)),
(ILIKE, binary_range_parser(ILike)),
(IN, (p, this) => p.parse_in(this)),
(IRLIKE, binary_range_parser(RegexpILike)),
(IS, (p, this) => p.parse_is(this)),
(LIKE, binary_range_parser(Like)),
(LT_AT, binary_range_parser(ArrayContainedBy)),
(OVERLAPS, binary_range_parser(Overlaps)),
(RLIKE, binary_range_parser(RegexpLike)),
(SIMILAR_TO, binary_range_parser(SimilarTo)),
(FOR, (p, this) => p.parse_comprehension(this)),
(QMARK_AMP, binary_range_parser(JSONBContainsAllTopKeys)),
(QMARK_PIPE, binary_range_parser(JSONBContainsAnyTopKeys)),
(HASH_DASH, binary_range_parser(JSONBDeleteAtPath)),
(AT_QMARK, binary_range_parser(JSONBPathExists)),
(ADJACENT, binary_range_parser(Adjacent)),
(OPERATOR, (p, this) => p.parse_operator(this)),
(AMP_LT, binary_range_parser(ExtendsLeft)),
(AMP_GT, binary_range_parser(ExtendsRight)),
])
let column_operators : Map[TokenType, ColumnOpFn?] = Map::from_array([
(DOT, None),
(
DOTCOLON,
Some(fn(p : Parser, this : Expr?, to : Expr?) raise SqlglotError {
Some(p.expression(mk(JSONCast, [("this", this), ("to", to)])))
}),
),
(
DCOLON,
Some(fn(p : Parser, this : Expr?, to : Expr?) raise SqlglotError {
Some(
p.build_cast(p.cfg.strict_cast, [
("this", this.map(x => Node(x))),
("to", to.map(x => Node(x))),
]),
)
}),
),
(ARROW, Some(build_json_extract_op)),
(DARROW, Some(build_json_extract_scalar_op)),
(HASH_ARROW, Some(build_jsonb_extract_op)),
(DHASH_ARROW, Some(build_jsonb_extract_scalar_op)),
(PLACEHOLDER, Some(build_jsonb_contains_top_key_op)),
])
let lambdas : Map[
TokenType,
(Parser, Array[Expr]) -> Expr? raise SqlglotError,
] = Map::from_array([
(
ARROW,
fn(p : Parser, expressions : Array[Expr]) raise SqlglotError {
let body = p.replace_lambda(p.parse_disjunction(), expressions)
Some(
p.expression(
mk(Lambda, [("this", body), ("expressions", expressions)]),
),
)
},
),
(
FARROW,
fn(p : Parser, expressions : Array[Expr]) raise SqlglotError {
let name = if expressions.is_empty() {
""
} else {
expressions[0].name()
}
let e = expr_or(p.parse_disjunction(), () => p.parse_select())
Some(p.expression(mk(Kwarg, [("this", var_(name)), ("expression", e)])))
},
),
])
let expression_parsers : Map[Kind, ParseFn] = Map::from_array([
(Cluster, p => p.parse_sort(Cluster, CLUSTER_BY)),
(Column, p => p.parse_column()),
(ColumnDef, p => p.parse_column_def(p.parse_column())),
(Condition, p => p.parse_disjunction()),
(DataType, p => p.parse_types(allow_identifiers=false, schema=true)),
// Python `exp.Expr`
(Expression, p => p.parse_expression()),
(From,p => p.parse_from(joins=true)),
(GrantPrincipal, p => p.parse_grant_principal()),
(GrantPrivilege, p => p.parse_grant_privilege()),
(Group, p => p.parse_group()),
(Having, p => p.parse_having()),
(Hint, p => p.parse_hint_body()),
(Identifier, p => p.parse_id_var()),
(Join, p => p.parse_join()),
(Lambda, p => p.parse_lambda()),
(Lateral, p => p.parse_lateral()),
(Limit, p => p.parse_limit()),
(Offset, p => p.parse_offset()),
(Order, p => p.parse_order()),
(Ordered, p => p.parse_ordered()),
(Properties, p => p.parse_properties()),
(PartitionedByProperty, p => p.parse_partitioned_by()),
(Qualify, p => p.parse_qualify()),
(Returning, p => p.parse_returning()),
(Select, p => p.parse_select()),
(Sort, p => p.parse_sort(Sort, SORT_BY)),
(Table, p => p.parse_table_parts()),
(TableAlias, p => p.parse_table_alias()),
(Tuple, p => p.parse_value(values=false)),
(Whens, p => p.parse_when_matched()),
(Where, p => p.parse_where()),
(Window, p => p.parse_named_window()),
(With, p => p.parse_with()),
])
let alter_parsers : Map[String, (Parser) -> Value? raise SqlglotError] = {
"ADD": p => Some(List(p.parse_alter_table_add().map(x => Node(x)))),
"AS": p => p.parse_select().map(x => Node(x)),
"ALTER": p => p.parse_alter_table_alter().map(x => Node(x)),
"CLUSTER BY": p => p.parse_cluster_property().map(x => Node(x)),
"DELETE": p => {
Some(Node(p.expression(mk(Delete, [("where", p.parse_where())]))))
},
"DROP": p => Some(List(p.parse_alter_table_drop().map(x => Node(x)))),
"RENAME": p => p.parse_alter_table_rename().map(x => Node(x)),
"SET": p => p.parse_alter_table_set().map(x => Node(x)),
"SWAP": p => {
let this = p.andv(p.match_(WITH), () => p.parse_table(schema=true))
Some(Node(p.expression(mk1(SwapTable, this))))
},
}
let alter_alter_parsers : Map[String, ParseFn] = {
"DISTKEY": p => p.parse_alter_diststyle(),
"DISTSTYLE": p => p.parse_alter_diststyle(),
"SORTKEY": p => p.parse_alter_sortkey(),
"COMPOUND": p => p.parse_alter_sortkey(compound=true),
}
let qm = fn(
key : String,
f : (Parser) -> Expr? raise SqlglotError,
) -> (Parser) -> (String, Value?) raise SqlglotError {
fn(p) { (key, f(p).map(x => Node(x))) }
}
let query_modifier_parsers : Map[
TokenType,
(Parser) -> (String, Value?) raise SqlglotError,
] = Map::from_array([
(MATCH_RECOGNIZE, qm("match", p => p.parse_match_recognize())),
(PREWHERE, qm("prewhere", p => p.parse_prewhere())),
(WHERE, qm("where", p => p.parse_where())),
(GROUP_BY, qm("group", p => p.parse_group())),
(HAVING, qm("having", p => p.parse_having())),
(QUALIFY, qm("qualify", p => p.parse_qualify())),
(
WINDOW,
fn(p : Parser) raise SqlglotError {
("windows", p.parse_window_clause().map(l => List(l.map(x => Node(x)))))
},
),
(ORDER_BY, qm("order", p => p.parse_order())),
(LIMIT, qm("limit", p => p.parse_limit())),
(FETCH, qm("limit", p => p.parse_limit())),
(OFFSET, qm("offset", p => p.parse_offset())),
(
FOR,
fn(p : Parser) raise SqlglotError {
("locks", Some(List(p.parse_locks().map(x => Node(x)))))
},
),
(
LOCK,
fn(p : Parser) raise SqlglotError {
("locks", Some(List(p.parse_locks().map(x => Node(x)))))
},
),
(TABLE_SAMPLE, qm("sample", p => p.parse_table_sample(as_modifier=true))),
(USING, qm("sample", p => p.parse_table_sample(as_modifier=true))),
(CLUSTER_BY, qm("cluster", p => Some(p.parse_cluster()))),
(
DISTRIBUTE_BY,
qm("distribute", p => p.parse_sort(Distribute, DISTRIBUTE_BY)),
),
(SORT_BY, qm("sort", p => p.parse_sort(Sort, SORT_BY))),
(CONNECT_BY, qm("connect", p => p.parse_connect(skip_start_token=true))),
])
let set_parsers : Map[String, ParseFn] = {
"GLOBAL": p => p.parse_set_item_assignment(kind="GLOBAL"),
"LOCAL": p => p.parse_set_item_assignment(kind="LOCAL"),
"SESSION": p => p.parse_set_item_assignment(kind="SESSION"),
"TRANSACTION": p => Some(p.parse_set_transaction()),
}
let type_literal_parsers : Map[
DType,
(Parser, Expr, Expr) -> Expr raise SqlglotError,
] = Map::from_array([
(
DType::JSON,
fn(p : Parser, this : Expr, _dt : Expr) raise SqlglotError {
p.expression(mk(ParseJSON, [("this", this), ("is_literal", true)]))
},
),
])
let pipe : Map[String, (Parser, Expr) -> Expr? raise SqlglotError] = {
"AGGREGATE": (p, q) => p.parse_pipe_syntax_aggregate(q),
"AS": (p, q) => {
let alias = p.parse_table_alias()
Some(p.build_pipe_cte(q, [mk0(Star)], alias_cte?=alias))
},
"DISTINCT": (p, q) => {
p.advance()
q.set("distinct", mk0(Distinct))
Some(q)
},
"EXTEND": (p, q) => p.parse_pipe_syntax_extend(q),
"LIMIT": (p, q) => p.parse_pipe_syntax_limit(q),
"ORDER BY": (p, q) => {
q.set("order", p.parse_order())
Some(q)
},
"PIVOT": (p, q) => p.parse_pipe_syntax_pivot(q),
"SELECT": (p, q) => p.parse_pipe_syntax_select(q),
"TABLESAMPLE": (p, q) => p.parse_pipe_syntax_tablesample(q),
"UNPIVOT": (p, q) => p.parse_pipe_syntax_pivot(q),
"WHERE": (p, q) => {
match p.parse_where() {
Some(w) =>
match q.arg("where") {
Some(existing) =>
q.set(
"where",
mk1(
Where,
and_(
[existing.this().unwrap(), w.this().unwrap()],
copy=false,
),
),
)
None => q.set("where", w)
}
None => ()
}
Some(q)
},
}
let analyze_expression_parsers : Map[String, ParseFn] = {
"ALL": p => p.parse_analyze_columns(),
"COMPUTE": p => p.parse_analyze_statistics(),
"DELETE": p => p.parse_analyze_delete(),
"DROP": p => p.parse_analyze_histogram(),
"ESTIMATE": p => p.parse_analyze_statistics(),
"LIST": p => p.parse_analyze_list(),
"PREDICATE": p => p.parse_analyze_columns(),
"UPDATE": p => p.parse_analyze_histogram(),
"VALIDATE": p => p.parse_analyze_validate(),
}
{
functions: base_functions(),
function_parsers,
no_paren_function_parsers,
statement_parsers,
unary_parsers,
string_parsers,
numeric_parsers,
primary_parsers,
placeholder_parsers,
range_parsers,
column_operators,
json_operators: Map([]),
lambdas,
expression_parsers,
property_parsers: base_property_parsers(),
constraint_parsers: base_constraint_parsers(),
alter_parsers,
alter_alter_parsers,
query_modifier_parsers,
set_parsers,
show_parsers: Map([]),
type_literal_parsers,
type_converters: Map([]),
pipe_syntax_transform_parsers: pipe,
analyze_expression_parsers,
hooks: ParserHooks::new(),
show_trie: WordTrie::new(),
set_trie: WordTrie::new(),
}
}
///|
pub fn Parser::replace_lambda(
self : Parser,
node : Expr?,
expressions : Array[Expr],
) -> Expr? raise SqlglotError {
let mut node = match node {
Some(n) => n
None => return None
}
let lambda_types : Map[String, Expr?] = Map([])
for e in expressions {
lambda_types[e.name()] = e.arg("to")
}
for column in node.find_all([Column]).collect() {
let parts = column.parts()
if parts.is_empty() {
continue
}
match lambda_types.get(parts[0].name()) {
Some(typ) => {
let mut dot_or_id = if !column.table_name().is_empty() {
column.to_dot()
} else {
column.this().unwrap()
}
match typ {
Some(t) =>
dot_or_id = self.expression(
mk(Cast, [("this", dot_or_id), ("to", t)]),
)
None => ()
}
let mut parent = column.parent
let mut replaced = false
while parent is Some(par) && par.kind == Dot {
let pp_is_dot = match par.parent {
Some(pp) => pp.kind == Dot
None => false
}
if !pp_is_dot {
par.replace(Some(dot_or_id)) |> ignore
replaced = true
break
}
parent = par.parent
}
if !replaced {
if physical_equal(column, node) {
node = dot_or_id
} else {
column.replace(Some(dot_or_id)) |> ignore
}
}
}
None => ()
}
}
Some(node)
}