-module(sqlode@model). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/sqlode/model.gleam"). -export([parse_engine/1, engine_to_string/1, parse_type_mapping/1, type_mapping_to_string/1, parse_runtime/1, runtime_to_string/1, empty_overrides/0, is_result_command/1, parse_query_command/1, query_command_to_string/1, parse_sql_type/1]). -export_type([engine/0, runtime/0, type_mapping/0, type_override/0, column_rename/0, overrides/0, gleam_output/0, sql_block/0, config/0, macro/0, parsed_query/0, scalar_type/0, enum_def/0, normalized_type/0, column/0, table/0, catalog/0, query_param/0, result_column/0, embedded_column/0, result_item/0, analyzed_query/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. -type engine() :: postgre_s_q_l | my_s_q_l | s_q_lite. -type runtime() :: raw | native. -type type_mapping() :: string_mapping | rich_mapping | strong_mapping. -type type_override() :: {db_type_override, binary(), binary(), gleam@option:option(boolean())} | {column_override, binary(), binary(), binary()}. -type column_rename() :: {column_rename, binary(), binary(), binary()}. -type overrides() :: {overrides, list(type_override()), list(column_rename())}. -type gleam_output() :: {gleam_output, binary(), runtime(), type_mapping(), boolean(), boolean(), boolean(), boolean(), boolean()}. -type sql_block() :: {sql_block, gleam@option:option(binary()), engine(), list(binary()), list(binary()), gleam_output(), overrides()}. -type config() :: {config, integer(), list(sql_block())}. -type macro() :: {macro_arg, integer(), binary()} | {macro_narg, integer(), binary()} | {macro_slice, integer(), binary()}. -type parsed_query() :: {parsed_query, binary(), binary(), sqlode@runtime:query_command(), binary(), binary(), integer(), list(macro())}. -type scalar_type() :: int_type | float_type | bool_type | string_type | bytes_type | date_time_type | date_type | time_type | uuid_type | json_type | {enum_type, binary()} | {custom_type, binary(), gleam@option:option(binary()), scalar_type()} | {array_type, scalar_type()}. -type enum_def() :: {enum_def, binary(), list(binary())}. -type normalized_type() :: {normalized_type, binary(), boolean()}. -type column() :: {column, binary(), scalar_type(), boolean()}. -type table() :: {table, binary(), list(column())}. -type catalog() :: {catalog, list(table()), list(enum_def())}. -type query_param() :: {query_param, integer(), binary(), scalar_type(), boolean(), boolean()}. -type result_column() :: {result_column, binary(), scalar_type(), boolean(), gleam@option:option(binary())}. -type embedded_column() :: {embedded_column, binary(), binary(), list(column())}. -type result_item() :: {scalar_result, result_column()} | {embedded_result, embedded_column()}. -type analyzed_query() :: {analyzed_query, parsed_query(), list(query_param()), list(result_item())}. -file("src/sqlode/model.gleam", 12). -spec parse_engine(binary()) -> {ok, engine()} | {error, binary()}. parse_engine(Value) -> case Value of <<"postgresql"/utf8>> -> {ok, postgre_s_q_l}; <<"mysql"/utf8>> -> {ok, my_s_q_l}; <<"sqlite"/utf8>> -> {ok, s_q_lite}; _ -> {error, <<"must be one of: postgresql, mysql, sqlite"/utf8>>} end. -file("src/sqlode/model.gleam", 21). -spec engine_to_string(engine()) -> binary(). engine_to_string(Engine) -> case Engine of postgre_s_q_l -> <<"postgresql"/utf8>>; my_s_q_l -> <<"mysql"/utf8>>; s_q_lite -> <<"sqlite"/utf8>> end. -file("src/sqlode/model.gleam", 40). -spec parse_type_mapping(binary()) -> {ok, type_mapping()} | {error, binary()}. parse_type_mapping(Value) -> case Value of <<"string"/utf8>> -> {ok, string_mapping}; <<"rich"/utf8>> -> {ok, rich_mapping}; <<"strong"/utf8>> -> {ok, strong_mapping}; _ -> {error, <<"must be one of: string, rich, strong"/utf8>>} end. -file("src/sqlode/model.gleam", 49). -spec type_mapping_to_string(type_mapping()) -> binary(). type_mapping_to_string(Mapping) -> case Mapping of string_mapping -> <<"string"/utf8>>; rich_mapping -> <<"rich"/utf8>>; strong_mapping -> <<"strong"/utf8>> end. -file("src/sqlode/model.gleam", 57). -spec parse_runtime(binary()) -> {ok, runtime()} | {error, binary()}. parse_runtime(Value) -> case Value of <<"raw"/utf8>> -> {ok, raw}; <<"based"/utf8>> -> {error, <<"\"based\" is not yet supported; use \"raw\" or \"native\" instead"/utf8>>}; <<"native"/utf8>> -> {ok, native}; _ -> {error, <<"must be one of: raw, native"/utf8>>} end. -file("src/sqlode/model.gleam", 67). -spec runtime_to_string(runtime()) -> binary(). runtime_to_string(Runtime) -> case Runtime of raw -> <<"raw"/utf8>>; native -> <<"native"/utf8>> end. -file("src/sqlode/model.gleam", 90). -spec empty_overrides() -> overrides(). empty_overrides() -> {overrides, [], []}. -file("src/sqlode/model.gleam", 138). -spec is_result_command(sqlode@runtime:query_command()) -> boolean(). is_result_command(Command) -> case Command of query_one -> true; query_many -> true; query_batch_one -> true; query_batch_many -> true; _ -> false end. -file("src/sqlode/model.gleam", 148). -spec parse_query_command(binary()) -> {ok, sqlode@runtime:query_command()} | {error, binary()}. parse_query_command(Value) -> case Value of <<":one"/utf8>> -> {ok, query_one}; <<":many"/utf8>> -> {ok, query_many}; <<":exec"/utf8>> -> {ok, query_exec}; <<":execresult"/utf8>> -> {ok, query_exec_result}; <<":execrows"/utf8>> -> {ok, query_exec_rows}; <<":execlastid"/utf8>> -> {ok, query_exec_last_id}; <<":batchone"/utf8>> -> {ok, query_batch_one}; <<":batchmany"/utf8>> -> {ok, query_batch_many}; <<":batchexec"/utf8>> -> {ok, query_batch_exec}; <<":copyfrom"/utf8>> -> {ok, query_copy_from}; _ -> {error, <<"must be one of: :one, :many, :exec, :execresult, :execrows, :execlastid, :batchone, :batchmany, :batchexec, :copyfrom"/utf8>>} end. -file("src/sqlode/model.gleam", 169). -spec query_command_to_string(sqlode@runtime:query_command()) -> binary(). query_command_to_string(Command) -> case Command of query_one -> <<"QueryOne"/utf8>>; query_many -> <<"QueryMany"/utf8>>; query_exec -> <<"QueryExec"/utf8>>; query_exec_result -> <<"QueryExecResult"/utf8>>; query_exec_rows -> <<"QueryExecRows"/utf8>>; query_exec_last_id -> <<"QueryExecLastId"/utf8>>; query_batch_one -> <<"QueryBatchOne"/utf8>>; query_batch_many -> <<"QueryBatchMany"/utf8>>; query_batch_exec -> <<"QueryBatchExec"/utf8>>; query_copy_from -> <<"QueryCopyFrom"/utf8>> end. -file("src/sqlode/model.gleam", 279). -spec strip_array_suffix(binary(), boolean()) -> {binary(), boolean()}. strip_array_suffix(Text, Seen) -> Trimmed = gleam@string:trim_end(Text), case gleam_stdlib:string_ends_with(Trimmed, <<"[]"/utf8>>) of true -> strip_array_suffix(gleam@string:drop_end(Trimmed, 2), true); false -> case gleam_stdlib:string_ends_with(Trimmed, <<" array"/utf8>>) of true -> strip_array_suffix(gleam@string:drop_end(Trimmed, 6), true); false -> {Trimmed, Seen} end end. -file("src/sqlode/model.gleam", 291). -spec strip_modifier(binary()) -> binary(). strip_modifier(Text) -> After_open = case gleam@string:split_once(Text, <<"("/utf8>>) of {ok, {Head, _}} -> Head; {error, nil} -> Text end, case gleam@string:split_once(After_open, <<")"/utf8>>) of {ok, {Head@1, _}} -> Head@1; {error, nil} -> After_open end. -file("src/sqlode/model.gleam", 306). -spec collapse_whitespace(binary()) -> binary(). collapse_whitespace(Text) -> _pipe = Text, _pipe@1 = gleam@string:split(_pipe, <<" "/utf8>>), _pipe@2 = gleam@list:filter(_pipe@1, fun(Part) -> Part /= <<""/utf8>> end), gleam@string:join(_pipe@2, <<" "/utf8>>). -file("src/sqlode/model.gleam", 265). -spec normalize_type_text(binary()) -> normalized_type(). normalize_type_text(Type_text) -> Lowered = begin _pipe = Type_text, _pipe@1 = string:lowercase(_pipe), gleam@string:trim(_pipe@1) end, {Without_array, Is_array} = strip_array_suffix(Lowered, false), Without_modifier = strip_modifier(Without_array), Base = begin _pipe@2 = Without_modifier, _pipe@3 = collapse_whitespace(_pipe@2), gleam@string:trim(_pipe@3) end, {normalized_type, Base, Is_array}. -file("src/sqlode/model.gleam", 313). -spec classify_builtin_type(binary()) -> {ok, scalar_type()} | {error, nil}. classify_builtin_type(Base) -> case Base of <<"int"/utf8>> -> {ok, int_type}; <<"int2"/utf8>> -> {ok, int_type}; <<"int4"/utf8>> -> {ok, int_type}; <<"int8"/utf8>> -> {ok, int_type}; <<"integer"/utf8>> -> {ok, int_type}; <<"smallint"/utf8>> -> {ok, int_type}; <<"bigint"/utf8>> -> {ok, int_type}; <<"mediumint"/utf8>> -> {ok, int_type}; <<"tinyint"/utf8>> -> {ok, int_type}; <<"serial"/utf8>> -> {ok, int_type}; <<"serial2"/utf8>> -> {ok, int_type}; <<"serial4"/utf8>> -> {ok, int_type}; <<"serial8"/utf8>> -> {ok, int_type}; <<"smallserial"/utf8>> -> {ok, int_type}; <<"bigserial"/utf8>> -> {ok, int_type}; <<"year"/utf8>> -> {ok, int_type}; <<"float"/utf8>> -> {ok, float_type}; <<"float4"/utf8>> -> {ok, float_type}; <<"float8"/utf8>> -> {ok, float_type}; <<"real"/utf8>> -> {ok, float_type}; <<"double"/utf8>> -> {ok, float_type}; <<"double precision"/utf8>> -> {ok, float_type}; <<"numeric"/utf8>> -> {ok, float_type}; <<"decimal"/utf8>> -> {ok, float_type}; <<"dec"/utf8>> -> {ok, float_type}; <<"money"/utf8>> -> {ok, float_type}; <<"smallmoney"/utf8>> -> {ok, float_type}; <<"bool"/utf8>> -> {ok, bool_type}; <<"boolean"/utf8>> -> {ok, bool_type}; <<"bytea"/utf8>> -> {ok, bytes_type}; <<"blob"/utf8>> -> {ok, bytes_type}; <<"longblob"/utf8>> -> {ok, bytes_type}; <<"mediumblob"/utf8>> -> {ok, bytes_type}; <<"tinyblob"/utf8>> -> {ok, bytes_type}; <<"binary"/utf8>> -> {ok, bytes_type}; <<"varbinary"/utf8>> -> {ok, bytes_type}; <<"uuid"/utf8>> -> {ok, uuid_type}; <<"uniqueidentifier"/utf8>> -> {ok, uuid_type}; <<"json"/utf8>> -> {ok, json_type}; <<"jsonb"/utf8>> -> {ok, json_type}; <<"timestamp"/utf8>> -> {ok, date_time_type}; <<"timestamptz"/utf8>> -> {ok, date_time_type}; <<"timestamp with time zone"/utf8>> -> {ok, date_time_type}; <<"timestamp without time zone"/utf8>> -> {ok, date_time_type}; <<"datetime"/utf8>> -> {ok, date_time_type}; <<"datetime2"/utf8>> -> {ok, date_time_type}; <<"date"/utf8>> -> {ok, date_type}; <<"time"/utf8>> -> {ok, time_type}; <<"timetz"/utf8>> -> {ok, time_type}; <<"time with time zone"/utf8>> -> {ok, time_type}; <<"time without time zone"/utf8>> -> {ok, time_type}; <<"interval"/utf8>> -> {ok, time_type}; <<"text"/utf8>> -> {ok, string_type}; <<"char"/utf8>> -> {ok, string_type}; <<"character"/utf8>> -> {ok, string_type}; <<"character varying"/utf8>> -> {ok, string_type}; <<"varchar"/utf8>> -> {ok, string_type}; <<"bpchar"/utf8>> -> {ok, string_type}; <<"nchar"/utf8>> -> {ok, string_type}; <<"nvarchar"/utf8>> -> {ok, string_type}; <<"clob"/utf8>> -> {ok, string_type}; <<"nclob"/utf8>> -> {ok, string_type}; <<"longtext"/utf8>> -> {ok, string_type}; <<"mediumtext"/utf8>> -> {ok, string_type}; <<"tinytext"/utf8>> -> {ok, string_type}; <<"string"/utf8>> -> {ok, string_type}; <<"name"/utf8>> -> {ok, string_type}; <<"citext"/utf8>> -> {ok, string_type}; <<"inet"/utf8>> -> {ok, string_type}; <<"cidr"/utf8>> -> {ok, string_type}; <<"macaddr"/utf8>> -> {ok, string_type}; <<"macaddr8"/utf8>> -> {ok, string_type}; <<"tsvector"/utf8>> -> {ok, string_type}; <<"tsquery"/utf8>> -> {ok, string_type}; <<"point"/utf8>> -> {ok, string_type}; <<"line"/utf8>> -> {ok, string_type}; <<"lseg"/utf8>> -> {ok, string_type}; <<"box"/utf8>> -> {ok, string_type}; <<"path"/utf8>> -> {ok, string_type}; <<"polygon"/utf8>> -> {ok, string_type}; <<"circle"/utf8>> -> {ok, string_type}; <<"xml"/utf8>> -> {ok, string_type}; <<"bit"/utf8>> -> {ok, string_type}; <<"bit varying"/utf8>> -> {ok, string_type}; <<"varbit"/utf8>> -> {ok, string_type}; _ -> {error, nil} end. -file("src/sqlode/model.gleam", 250). ?DOC( " Classify by exact compound name first (e.g. \"timestamp with time zone\"),\n" " then fall back to the first whitespace-separated token so trailing\n" " garbage from unsupported column clauses (GENERATED, PARTITION BY) does\n" " not block recognition of the primary type keyword.\n" ). -spec classify_normalized_base(binary()) -> {ok, scalar_type()} | {error, nil}. classify_normalized_base(Base) -> case classify_builtin_type(Base) of {ok, T} -> {ok, T}; {error, nil} -> case gleam@string:split_once(Base, <<" "/utf8>>) of {ok, {First_word, _}} -> classify_builtin_type(First_word); {error, nil} -> {error, nil} end end. -file("src/sqlode/model.gleam", 234). ?DOC( " Parse a SQL type name into a ScalarType by normalizing the type token\n" " (lowercasing, stripping modifiers and array markers, collapsing whitespace)\n" " and matching the base name against a fixed table of built-ins.\n" "\n" " Used by both schema parsing (CREATE TABLE column types) and query analysis\n" " (PostgreSQL type casts like `$1::int`). Returns Error(Nil) for unrecognized\n" " types so callers can surface the original text in a precise diagnostic\n" " instead of guessing.\n" ). -spec parse_sql_type(binary()) -> {ok, scalar_type()} | {error, nil}. parse_sql_type(Type_text) -> Normalized = normalize_type_text(Type_text), case classify_normalized_base(erlang:element(2, Normalized)) of {ok, Element_type} -> case erlang:element(3, Normalized) of true -> {ok, {array_type, Element_type}}; false -> {ok, Element_type} end; {error, nil} -> {error, nil} end.