-module(sqlode@internal@generate). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/sqlode/internal/generate.gleam"). -export([disambiguate_param_names/1, apply_column_renames/2, generate_config/1, run/1, error_to_string/1]). -export_type([generate_error/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. ?MODULEDOC(false). -type generate_error() :: {config_error, sqlode@internal@config:config_error()} | {schema_read_error, binary(), binary()} | {schema_parse_error, binary()} | {query_read_error, binary(), binary()} | {query_parse_error, binary(), binary()} | {query_analysis_error, binary()} | {no_queries_generated, binary(), integer(), integer(), list(binary()), list(binary())} | {duplicate_query_name, binary(), list(binary())} | {normalized_query_name_collision, binary(), list(binary()), list(binary())} | {unsupported_annotation, binary(), binary(), binary()} | {invalid_out_path, binary()} | {write_error, sqlode@internal@writer:write_error()} | vendor_runtime_not_found | {unsupported_array_for_engine, binary(), binary()}. -file("src/sqlode/internal/generate.gleam", 80). ?DOC(false). -spec resolve_path(binary(), binary()) -> binary(). resolve_path(Base_dir, Path) -> case filepath:is_absolute(Path) of true -> Path; false -> case filepath:expand(filepath:join(Base_dir, Path)) of {ok, Expanded} -> Expanded; {error, _} -> filepath:join(Base_dir, Path) end end. -file("src/sqlode/internal/generate.gleam", 65). ?DOC(false). -spec resolve_config_paths(sqlode@internal@model:config(), binary()) -> sqlode@internal@model:config(). resolve_config_paths(Cfg, Base_dir) -> Sql = gleam@list:map( erlang:element(3, Cfg), fun(Block) -> Schema = gleam@list:map( erlang:element(4, Block), fun(_capture) -> resolve_path(Base_dir, _capture) end ), Queries = gleam@list:map( erlang:element(5, Block), fun(_capture@1) -> resolve_path(Base_dir, _capture@1) end ), Gleam = begin _record = erlang:element(6, Block), {gleam_output, resolve_path( Base_dir, erlang:element(2, erlang:element(6, Block)) ), erlang:element(3, _record), erlang:element(4, _record), erlang:element(5, _record), erlang:element(6, _record), erlang:element(7, _record), erlang:element(8, _record), erlang:element(9, _record), erlang:element(10, _record)} end, {sql_block, erlang:element(2, Block), erlang:element(3, Block), Schema, Queries, Gleam, erlang:element(7, Block)} end ), {config, erlang:element(2, Cfg), Sql}. -file("src/sqlode/internal/generate.gleam", 305). ?DOC(false). -spec read_runtime_source() -> {ok, binary()} | {error, nil}. read_runtime_source() -> Candidates = [<<"src/sqlode/runtime.gleam"/utf8>>, <<"build/packages/sqlode/src/sqlode/runtime.gleam"/utf8>>, <<"build/dev/erlang/sqlode/src/sqlode/runtime.gleam"/utf8>>], gleam@list:find_map(Candidates, fun(Path) -> case simplifile:read(Path) of {ok, Content} -> {ok, Content}; {error, _} -> {error, nil} end end). -file("src/sqlode/internal/generate.gleam", 344). ?DOC(false). -spec collect_referenced_tables(list(sqlode@internal@model:analyzed_query())) -> list(binary()). collect_referenced_tables(Queries) -> _pipe = Queries, _pipe@1 = gleam@list:flat_map( _pipe, fun(Query) -> gleam@list:flat_map( erlang:element(4, Query), fun(Item) -> case Item of {scalar_result, Col} -> case erlang:element(5, Col) of {some, Name} -> [Name]; none -> [] end; {embedded_result, Embed} -> [erlang:element(3, Embed)] end end ) end ), gleam@list:unique(_pipe@1). -file("src/sqlode/internal/generate.gleam", 396). ?DOC(false). -spec enum_name(sqlode@internal@model:scalar_type()) -> {ok, binary()} | {error, nil}. enum_name(Scalar_type) -> case Scalar_type of {enum_type, Name} -> {ok, Name}; {array_type, Inner} -> enum_name(Inner); _ -> {error, nil} end. -file("src/sqlode/internal/generate.gleam", 361). ?DOC(false). -spec collect_referenced_enums( list(sqlode@internal@model:table()), list(sqlode@internal@model:analyzed_query()) ) -> list(binary()). collect_referenced_enums(Retained_tables, Queries) -> From_tables = begin _pipe = Retained_tables, _pipe@1 = gleam@list:flat_map(_pipe, fun(T) -> erlang:element(3, T) end), gleam@list:filter_map( _pipe@1, fun(C) -> enum_name(erlang:element(3, C)) end ) end, From_query_columns = begin _pipe@2 = Queries, gleam@list:flat_map( _pipe@2, fun(Q) -> gleam@list:flat_map( erlang:element(4, Q), fun(Item) -> case Item of {scalar_result, Col} -> case enum_name(erlang:element(3, Col)) of {ok, Name} -> [Name]; {error, nil} -> [] end; {embedded_result, Embed} -> gleam@list:filter_map( erlang:element(4, Embed), fun(C@1) -> enum_name(erlang:element(3, C@1)) end ) end end ) end ) end, From_query_params = begin _pipe@3 = Queries, gleam@list:flat_map( _pipe@3, fun(Q@1) -> gleam@list:filter_map( erlang:element(3, Q@1), fun(P) -> enum_name(erlang:element(4, P)) end ) end ) end, _pipe@4 = lists:append( From_tables, lists:append(From_query_columns, From_query_params) ), gleam@list:unique(_pipe@4). -file("src/sqlode/internal/generate.gleam", 326). ?DOC(false). -spec prune_catalog_to_used( sqlode@internal@model:catalog(), list(sqlode@internal@model:analyzed_query()) ) -> sqlode@internal@model:catalog(). prune_catalog_to_used(Catalog, Queries) -> Referenced_tables = collect_referenced_tables(Queries), Retained_tables = gleam@list:filter( erlang:element(2, Catalog), fun(T) -> gleam@list:contains(Referenced_tables, erlang:element(2, T)) end ), Referenced_enums = collect_referenced_enums(Retained_tables, Queries), Retained_enums = gleam@list:filter( erlang:element(3, Catalog), fun(E) -> gleam@list:contains(Referenced_enums, erlang:element(2, E)) end ), {catalog, Retained_tables, Retained_enums}. -file("src/sqlode/internal/generate.gleam", 213). ?DOC(false). -spec base_output_files( sqlode@internal@naming:naming_context(), sqlode@internal@model:sql_block(), list(sqlode@internal@model:analyzed_query()), sqlode@internal@model:catalog(), gleam@dict:dict(binary(), binary()) ) -> {ok, list(sqlode@internal@writer:generated_file())} | {error, generate_error()}. base_output_files(Naming_ctx, Block, Analyzed, Catalog, Table_matches) -> {sql_block, _, _, _, _, Gleam, _} = Block, {gleam_output, Out, _, _, _, _, _, _, _, _} = Gleam, Has_row_types = gleam@list:any( Analyzed, fun(Query) -> sqlode@internal@model:is_result_command( erlang:element(4, erlang:element(2, Query)) ) andalso not gleam@list:is_empty(erlang:element(4, Query)) end ), Has_models = Has_row_types orelse not gleam@list:is_empty( erlang:element(2, Catalog) ), Files = [{generated_file, Out, <<"params.gleam"/utf8>>, sqlode@internal@codegen@params:render( Naming_ctx, Analyzed, erlang:element(4, Gleam), sqlode@internal@codegen@common:out_to_module_path(Out), sqlode@internal@codegen@common:runtime_import_path(Gleam) )}, {generated_file, Out, <<"queries.gleam"/utf8>>, sqlode@internal@codegen@queries:render( Naming_ctx, Block, Analyzed, Table_matches, erlang:element(6, Gleam) )}], Files_with_models = case Has_models of true -> Effective_catalog = case erlang:element(7, Gleam) of true -> prune_catalog_to_used(Catalog, Analyzed); false -> Catalog end, lists:append( Files, [{generated_file, Out, <<"models.gleam"/utf8>>, sqlode@internal@codegen@models:render( Naming_ctx, Effective_catalog, Analyzed, Table_matches, erlang:element(4, Gleam), erlang:element(6, Gleam) )}] ); false -> Files end, case erlang:element(8, Gleam) of true -> case read_runtime_source() of {ok, Source} -> {ok, lists:append( Files_with_models, [{generated_file, Out, <<"runtime.gleam"/utf8>>, Source}] )}; {error, _} -> {error, vendor_runtime_not_found} end; false -> {ok, Files_with_models} end. -file("src/sqlode/internal/generate.gleam", 404). ?DOC(false). -spec load_catalog(list(binary()), sqlode@internal@model:engine(), boolean()) -> {ok, sqlode@internal@model:catalog()} | {error, generate_error()}. load_catalog(Paths, Engine, Strict_views) -> gleam@result:'try'( sqlode@internal@sql_paths:expand( Paths, fun(Path, Detail) -> {schema_read_error, Path, Detail} end ), fun(Expanded) -> gleam@result:'try'( begin _pipe = Expanded, gleam@list:try_map( _pipe, fun(Path@1) -> _pipe@1 = simplifile:read(Path@1), _pipe@2 = gleam@result:map( _pipe@1, fun(Content) -> {Path@1, Content} end ), gleam@result:map_error( _pipe@2, fun(Error) -> {schema_read_error, Path@1, <<"Failed to read schema file: "/utf8, (simplifile:describe_error(Error))/binary>>} end ) end ) end, fun(Entries) -> gleam@result:'try'( begin _pipe@3 = sqlode@internal@schema_parser:parse_files_with_engine( Entries, Engine ), gleam@result:map_error( _pipe@3, fun(Error@1) -> {schema_parse_error, sqlode@internal@schema_parser:error_to_string( Error@1 )} end ) end, fun(_use0) -> {Catalog, Warnings} = _use0, case {Strict_views, Warnings} of {true, [_ | _]} -> Detail@1 = <<"strict_views is enabled but the schema produced resolution warnings:\n "/utf8, (gleam@string:join( gleam@list:map( Warnings, fun sqlode@internal@schema_parser:warning_to_string/1 ), <<"\n "/utf8>> ))/binary>>, {error, {schema_parse_error, Detail@1}}; {_, _} -> gleam@list:each( Warnings, fun(W) -> gleam_stdlib:println_error( sqlode@internal@schema_parser:warning_to_string( W ) ) end ), {ok, Catalog} end end ) end ) end ). -file("src/sqlode/internal/generate.gleam", 495). ?DOC(false). -spec wrap_validation_error(sqlode@internal@query_validation:validation_error()) -> generate_error(). wrap_validation_error(Err) -> case Err of {duplicate_name, Name, Paths} -> {duplicate_query_name, Name, Paths}; {normalized_name_collision, Function_name, Names, Paths@1} -> {normalized_query_name_collision, Function_name, Names, Paths@1}; {unsupported_annotation, Query_name, Command, Detail} -> {unsupported_annotation, Query_name, Command, Detail}; {unsupported_array_for_engine, Query_name@1, Engine} -> {unsupported_array_for_engine, Query_name@1, Engine} end. -file("src/sqlode/internal/generate.gleam", 487). ?DOC(false). -spec validate_no_duplicate_query_names( list(sqlode@internal@query_ir:tokenized_query()) ) -> {ok, list(sqlode@internal@query_ir:tokenized_query())} | {error, generate_error()}. validate_no_duplicate_query_names(Queries) -> _pipe = sqlode@internal@query_validation:validate_no_duplicate_names( Queries ), _pipe@1 = gleam@result:map_error(_pipe, fun wrap_validation_error/1), gleam@result:map(_pipe@1, fun(_) -> Queries end). -file("src/sqlode/internal/generate.gleam", 453). ?DOC(false). -spec load_queries( sqlode@internal@naming:naming_context(), sqlode@internal@model:sql_block() ) -> {ok, list(sqlode@internal@query_ir:tokenized_query())} | {error, generate_error()}. load_queries(Naming_ctx, Block) -> {sql_block, _, Engine, _, Queries, _, _} = Block, gleam@result:'try'( sqlode@internal@sql_paths:expand( Queries, fun(Path, Detail) -> {query_read_error, Path, Detail} end ), fun(Expanded) -> _pipe = Expanded, _pipe@3 = gleam@list:try_map( _pipe, fun(Path@1) -> gleam@result:'try'( begin _pipe@1 = simplifile:read(Path@1), gleam@result:map_error( _pipe@1, fun(Error) -> {query_read_error, Path@1, <<"Failed to read query file: "/utf8, (simplifile:describe_error(Error))/binary>>} end ) end, fun(Content) -> _pipe@2 = sqlode@internal@query_parser:parse_file( Path@1, Engine, Naming_ctx, Content ), gleam@result:map_error( _pipe@2, fun(Error@1) -> {query_parse_error, Path@1, sqlode@internal@query_parser:error_to_string( Error@1 )} end ) end ) end ), _pipe@4 = gleam@result:map(_pipe@3, fun lists:append/1), gleam@result:'try'(_pipe@4, fun validate_no_duplicate_query_names/1) end ). -file("src/sqlode/internal/generate.gleam", 555). ?DOC(false). -spec find_column_override( binary(), binary(), list(sqlode@internal@model:type_override()) ) -> {ok, binary()} | {error, nil}. find_column_override(Table_name, Column_name, Overrides) -> gleam@list:find_map(Overrides, fun(Ovr) -> case Ovr of {column_override, Table, Column, Gleam_type} -> case (string:lowercase(Table) =:= string:lowercase( Table_name )) andalso (string:lowercase(Column) =:= string:lowercase( Column_name )) of true -> {ok, Gleam_type}; false -> {error, nil} end; {db_type_override, _, _, _} -> {error, nil} end end). -file("src/sqlode/internal/generate.gleam", 575). ?DOC(false). -spec find_db_type_override( sqlode@internal@model:scalar_type(), boolean(), list(sqlode@internal@model:type_override()) ) -> {ok, binary()} | {error, nil}. find_db_type_override(Scalar_type, Is_nullable, Overrides) -> Type_name = sqlode@internal@type_mapping:scalar_type_to_db_name(Scalar_type), gleam@list:find_map(Overrides, fun(Ovr) -> case Ovr of {db_type_override, Db_type, Gleam_type, Nullable} -> case string:lowercase(Db_type) =:= Type_name of true -> case Nullable of none -> {ok, Gleam_type}; {some, N} -> case N =:= Is_nullable of true -> {ok, Gleam_type}; false -> {error, nil} end end; false -> {error, nil} end; {column_override, _, _, _} -> {error, nil} end end). -file("src/sqlode/internal/generate.gleam", 634). ?DOC(false). -spec parse_module_qualified_type(binary()) -> {gleam@option:option(binary()), binary()}. parse_module_qualified_type(Gleam_type) -> case gleam@string:split_once(Gleam_type, <<"."/utf8>>) of {ok, {Module_path, Type_name}} -> {{some, Module_path}, Type_name}; {error, _} -> {none, Gleam_type} end. -file("src/sqlode/internal/generate.gleam", 602). ?DOC(false). -spec gleam_type_to_scalar(binary(), sqlode@internal@model:scalar_type()) -> sqlode@internal@model:scalar_type(). gleam_type_to_scalar(Gleam_type, Underlying) -> case string:lowercase(Gleam_type) of <<"int"/utf8>> -> int_type; <<"float"/utf8>> -> float_type; <<"bool"/utf8>> -> bool_type; <<"string"/utf8>> -> string_type; <<"bitarray"/utf8>> -> bytes_type; _ -> {Module, Type_name} = parse_module_qualified_type(Gleam_type), Underlying_name = sqlode@internal@type_mapping:scalar_type_to_gleam_type( Underlying, string_mapping ), gleam_stdlib:println_error( <<<<<<<<<<<<<<<<<<<<"Warning: custom gleam_type \""/utf8, Gleam_type/binary>>/binary, "\" will use the encoder/decoder for the underlying \""/utf8>>/binary, Underlying_name/binary>>/binary, "\" type. Ensure \""/utf8>>/binary, Type_name/binary>>/binary, "\" is defined as a transparent type alias (e.g., pub type "/utf8>>/binary, Type_name/binary>>/binary, " = "/utf8>>/binary, Underlying_name/binary>>/binary, "). Opaque types are not supported."/utf8>> ), {custom_type, Type_name, Module, Underlying} end. -file("src/sqlode/internal/generate.gleam", 508). ?DOC(false). -spec apply_type_overrides( sqlode@internal@model:catalog(), list(sqlode@internal@model:type_override()) ) -> sqlode@internal@model:catalog(). apply_type_overrides(Catalog, Overrides) -> case Overrides of [] -> Catalog; _ -> Tables = gleam@list:map( erlang:element(2, Catalog), fun(Table) -> Columns = gleam@list:map( erlang:element(3, Table), fun(Col) -> case find_column_override( erlang:element(2, Table), erlang:element(2, Col), Overrides ) of {ok, Gleam_type} -> {column, erlang:element(2, Col), gleam_type_to_scalar( Gleam_type, erlang:element(3, Col) ), erlang:element(4, Col)}; {error, _} -> case find_db_type_override( erlang:element(3, Col), erlang:element(4, Col), Overrides ) of {ok, Gleam_type@1} -> {column, erlang:element(2, Col), gleam_type_to_scalar( Gleam_type@1, erlang:element(3, Col) ), erlang:element(4, Col)}; {error, _} -> Col end end end ), {table, erlang:element(2, Table), Columns} end ), {catalog, Tables, erlang:element(3, Catalog)} end. -file("src/sqlode/internal/generate.gleam", 117). ?DOC(false). -spec load_and_prepare_catalog(sqlode@internal@model:sql_block()) -> {ok, sqlode@internal@model:catalog()} | {error, generate_error()}. load_and_prepare_catalog(Block) -> gleam@result:'try'( load_catalog( erlang:element(4, Block), erlang:element(3, Block), erlang:element(9, erlang:element(6, Block)) ), fun(Raw_catalog) -> {ok, apply_type_overrides( Raw_catalog, erlang:element(2, erlang:element(7, Block)) )} end ). -file("src/sqlode/internal/generate.gleam", 659). ?DOC(false). -spec disambiguate_query_params(sqlode@internal@model:analyzed_query()) -> sqlode@internal@model:analyzed_query(). disambiguate_query_params(Query) -> Counts = gleam@list:fold( erlang:element(3, Query), maps:new(), fun(Acc, P) -> gleam@dict:upsert( Acc, erlang:element(3, P), fun(Prev) -> case Prev of {some, N} -> N + 1; none -> 1 end end ) end ), {_, Renamed_reversed} = gleam@list:fold( erlang:element(3, Query), {maps:new(), []}, fun(Acc@1, P@1) -> {Seen, Out} = Acc@1, case gleam_stdlib:map_get(Counts, erlang:element(3, P@1)) of {ok, N@1} when N@1 > 1 -> Occurrence = case gleam_stdlib:map_get( Seen, erlang:element(3, P@1) ) of {ok, Prev@1} -> Prev@1 + 1; {error, _} -> 1 end, New_seen = gleam@dict:insert( Seen, erlang:element(3, P@1), Occurrence ), New_name = case Occurrence of 1 -> erlang:element(3, P@1); _ -> <<<<(erlang:element(3, P@1))/binary, "_"/utf8>>/binary, (erlang:integer_to_binary(Occurrence))/binary>> end, Renamed = {query_param, erlang:element(2, P@1), New_name, erlang:element(4, P@1), erlang:element(5, P@1), erlang:element(6, P@1)}, {New_seen, [Renamed | Out]}; _ -> {Seen, [P@1 | Out]} end end ), {analyzed_query, erlang:element(2, Query), lists:reverse(Renamed_reversed), erlang:element(4, Query)}. -file("src/sqlode/internal/generate.gleam", 653). ?DOC(false). -spec disambiguate_param_names(list(sqlode@internal@model:analyzed_query())) -> list(sqlode@internal@model:analyzed_query()). disambiguate_param_names(Queries) -> gleam@list:map(Queries, fun disambiguate_query_params/1). -file("src/sqlode/internal/generate.gleam", 722). ?DOC(false). -spec find_column_rename( binary(), gleam@option:option(binary()), list(sqlode@internal@model:column_rename()) ) -> {ok, binary()} | {error, nil}. find_column_rename(Column_name, Source_table, Renames) -> gleam@list:find_map( Renames, fun(R) -> Column_matches = string:lowercase(erlang:element(3, R)) =:= string:lowercase( Column_name ), Table_matches = case Source_table of {some, Table} -> string:lowercase(erlang:element(2, R)) =:= string:lowercase( Table ); none -> false end, case Column_matches andalso Table_matches of true -> {ok, erlang:element(4, R)}; false -> {error, nil} end end ). -file("src/sqlode/internal/generate.gleam", 695). ?DOC(false). -spec apply_column_renames( list(sqlode@internal@model:analyzed_query()), list(sqlode@internal@model:column_rename()) ) -> list(sqlode@internal@model:analyzed_query()). apply_column_renames(Queries, Renames) -> case Renames of [] -> Queries; _ -> gleam@list:map( Queries, fun(Query) -> Result_columns = gleam@list:map( erlang:element(4, Query), fun(Item) -> case Item of {scalar_result, Col} -> case find_column_rename( erlang:element(2, Col), erlang:element(5, Col), Renames ) of {ok, New_name} -> {scalar_result, {result_column, New_name, erlang:element(3, Col), erlang:element(4, Col), erlang:element(5, Col)}}; {error, _} -> Item end; {embedded_result, _} -> Item end end ), {analyzed_query, erlang:element(2, Query), erlang:element(3, Query), Result_columns} end ) end. -file("src/sqlode/internal/generate.gleam", 741). ?DOC(false). -spec validate_out_path(binary()) -> {ok, nil} | {error, generate_error()}. validate_out_path(Out) -> Module_path = sqlode@internal@codegen@common:out_to_module_path(Out), case gleam_stdlib:string_starts_with(Module_path, <<"/"/utf8>>) orelse gleam_stdlib:string_starts_with( Module_path, <<"."/utf8>> ) of true -> {error, {invalid_out_path, Out}}; false -> {ok, nil} end. -file("src/sqlode/internal/generate.gleam", 751). ?DOC(false). -spec validate_unsupported_annotations( list(sqlode@internal@model:analyzed_query()) ) -> {ok, nil} | {error, generate_error()}. validate_unsupported_annotations(Queries) -> _pipe = sqlode@internal@query_validation:validate_unsupported_annotations( Queries ), gleam@result:map_error(_pipe, fun wrap_validation_error/1). -file("src/sqlode/internal/generate.gleam", 758). ?DOC(false). -spec validate_array_engine_support( sqlode@internal@model:engine(), list(sqlode@internal@model:analyzed_query()) ) -> {ok, nil} | {error, generate_error()}. validate_array_engine_support(Engine, Queries) -> _pipe = sqlode@internal@query_validation:validate_array_engine_support( Engine, Queries ), gleam@result:map_error(_pipe, fun wrap_validation_error/1). -file("src/sqlode/internal/generate.gleam", 128). ?DOC(false). -spec load_and_analyze_queries( sqlode@internal@naming:naming_context(), sqlode@internal@model:sql_block(), sqlode@internal@model:catalog() ) -> {ok, {list(sqlode@internal@query_ir:tokenized_query()), list(sqlode@internal@model:analyzed_query())}} | {error, generate_error()}. load_and_analyze_queries(Naming_ctx, Block, Catalog) -> gleam@result:'try'( load_queries(Naming_ctx, Block), fun(Queries) -> gleam@result:'try'( begin _pipe = sqlode@internal@query_analyzer:analyze_queries( erlang:element(3, Block), Catalog, Naming_ctx, Queries ), gleam@result:map_error( _pipe, fun(Error) -> {query_analysis_error, sqlode@internal@query_analyzer:analysis_error_to_string( Error, erlang:element(3, Block) )} end ) end, fun(Analyzed) -> gleam@result:'try'( validate_unsupported_annotations(Analyzed), fun(_use0) -> nil = _use0, gleam@result:'try'( validate_array_engine_support( erlang:element(3, Block), Analyzed ), fun(_use0@1) -> nil = _use0@1, Analyzed@1 = apply_column_renames( Analyzed, erlang:element( 3, erlang:element(7, Block) ) ), Analyzed@2 = disambiguate_param_names( Analyzed@1 ), {ok, {Queries, Analyzed@2}} end ) end ) end ) end ). -file("src/sqlode/internal/generate.gleam", 766). ?DOC(false). -spec validate_native_annotations(list(sqlode@internal@model:analyzed_query())) -> {ok, nil} | {error, generate_error()}. validate_native_annotations(Queries) -> _pipe = sqlode@internal@query_validation:validate_native_annotations( Queries ), gleam@result:map_error(_pipe, fun wrap_validation_error/1). -file("src/sqlode/internal/generate.gleam", 773). ?DOC(false). -spec adapter_filename(sqlode@internal@model:engine()) -> binary(). adapter_filename(Engine) -> case Engine of postgre_s_q_l -> <<"pog_adapter.gleam"/utf8>>; s_q_lite -> <<"sqlight_adapter.gleam"/utf8>>; my_s_q_l -> <<"mysql_adapter.gleam"/utf8>> end. -file("src/sqlode/internal/generate.gleam", 845). ?DOC(false). -spec guard_result(boolean(), fun(() -> {ok, RBG} | {error, nil})) -> {ok, RBG} | {error, nil}. guard_result(Condition, Next) -> case Condition of true -> Next(); false -> {error, nil} end. -file("src/sqlode/internal/generate.gleam", 852). ?DOC(false). -spec find_table(sqlode@internal@model:catalog(), binary()) -> {ok, sqlode@internal@model:table()} | {error, nil}. find_table(Catalog, Table_name) -> gleam@list:find( erlang:element(2, Catalog), fun(T) -> string:lowercase(erlang:element(2, T)) =:= string:lowercase( Table_name ) end ). -file("src/sqlode/internal/generate.gleam", 861). ?DOC(false). -spec columns_match( list(sqlode@internal@model:result_item()), list(sqlode@internal@model:column()) ) -> boolean(). columns_match(Result_columns, Table_columns) -> case erlang:length(Result_columns) =:= erlang:length(Table_columns) of false -> false; true -> _pipe = gleam@list:zip(Result_columns, Table_columns), gleam@list:all( _pipe, fun(Pair) -> {Rc, Tc} = Pair, case Rc of {scalar_result, {result_column, Name, Scalar_type, Nullable, _}} -> ((string:lowercase(Name) =:= string:lowercase( erlang:element(2, Tc) )) andalso (Scalar_type =:= erlang:element(3, Tc))) andalso (Nullable =:= erlang:element(4, Tc)); {embedded_result, _} -> false end end ) end. -file("src/sqlode/internal/generate.gleam", 796). ?DOC(false). -spec try_match_query_to_table( sqlode@internal@naming:naming_context(), sqlode@internal@model:catalog(), sqlode@internal@model:analyzed_query(), boolean() ) -> {ok, {binary(), binary()}} | {error, nil}. try_match_query_to_table(Naming_ctx, Catalog, Query, Emit_exact_table_names) -> guard_result( sqlode@internal@model:is_result_command( erlang:element(4, erlang:element(2, Query)) ), fun() -> Has_embed = gleam@list:any( erlang:element(4, Query), fun(Col) -> case Col of {embedded_result, _} -> true; {scalar_result, _} -> false end end ), guard_result( not Has_embed, fun() -> gleam@result:'try'(case erlang:element(4, Query) of [{scalar_result, {result_column, _, _, _, {some, Name}}} | _] -> {ok, Name}; _ -> {error, nil} end, fun(Table_name) -> All_same_table = gleam@list:all( erlang:element(4, Query), fun(Col@1) -> case Col@1 of {scalar_result, {result_column, _, _, _, Src}} -> Src =:= {some, Table_name}; {embedded_result, _} -> false end end ), guard_result( All_same_table, fun() -> gleam@result:'try'( find_table(Catalog, Table_name), fun(Table) -> guard_result( columns_match( erlang:element(4, Query), erlang:element(3, Table) ), fun() -> {ok, {erlang:element( 3, erlang:element( 2, Query ) ), sqlode@internal@naming:table_type_name( Naming_ctx, Table_name, Emit_exact_table_names )}} end ) end ) end ) end) end ) end ). -file("src/sqlode/internal/generate.gleam", 783). ?DOC(false). -spec compute_table_matches( sqlode@internal@naming:naming_context(), sqlode@internal@model:catalog(), list(sqlode@internal@model:analyzed_query()), boolean() ) -> gleam@dict:dict(binary(), binary()). compute_table_matches(Naming_ctx, Catalog, Queries, Emit_exact_table_names) -> _pipe = Queries, _pipe@1 = gleam@list:filter_map( _pipe, fun(Query) -> try_match_query_to_table( Naming_ctx, Catalog, Query, Emit_exact_table_names ) end ), maps:from_list(_pipe@1). -file("src/sqlode/internal/generate.gleam", 153). ?DOC(false). -spec render_output_files( sqlode@internal@naming:naming_context(), sqlode@internal@model:sql_block(), sqlode@internal@model:catalog(), list(sqlode@internal@query_ir:tokenized_query()), list(sqlode@internal@model:analyzed_query()) ) -> {ok, list(sqlode@internal@writer:generated_file())} | {error, generate_error()}. render_output_files(Naming_ctx, Block, Catalog, Queries, Analyzed) -> {sql_block, _, _, _, _, Gleam, _} = Block, {gleam_output, Out, _, _, _, _, _, _, _, _} = Gleam, case Analyzed of [] -> {error, {no_queries_generated, Out, erlang:length(Queries), erlang:length(erlang:element(2, Catalog)), erlang:element(5, Block), erlang:element(4, Block)}}; _ -> Table_matches = compute_table_matches( Naming_ctx, Catalog, Analyzed, erlang:element(6, Gleam) ), gleam@result:'try'( base_output_files( Naming_ctx, Block, Analyzed, Catalog, Table_matches ), fun(Base_files) -> case erlang:element(3, Gleam) of raw -> {ok, Base_files}; native -> gleam@result:'try'( validate_native_annotations(Analyzed), fun(_use0) -> nil = _use0, {ok, lists:append( Base_files, [{generated_file, Out, adapter_filename( erlang:element(3, Block) ), sqlode@internal@codegen@adapter:render( Naming_ctx, Block, Analyzed, Table_matches )}] )} end ) end end ) end. -file("src/sqlode/internal/generate.gleam", 103). ?DOC(false). -spec generate_sql_block( sqlode@internal@naming:naming_context(), sqlode@internal@model:sql_block() ) -> {ok, list(sqlode@internal@writer:generated_file())} | {error, generate_error()}. generate_sql_block(Naming_ctx, Block) -> gleam@result:'try'( validate_out_path(erlang:element(2, erlang:element(6, Block))), fun(_use0) -> nil = _use0, gleam@result:'try'( load_and_prepare_catalog(Block), fun(Catalog) -> gleam@result:'try'( load_and_analyze_queries(Naming_ctx, Block, Catalog), fun(_use0@1) -> {Queries, Analyzed} = _use0@1, render_output_files( Naming_ctx, Block, Catalog, Queries, Analyzed ) end ) end ) end ). -file("src/sqlode/internal/generate.gleam", 91). ?DOC(false). -spec generate_config(sqlode@internal@model:config()) -> {ok, list(binary())} | {error, generate_error()}. generate_config(Cfg) -> Naming_ctx = sqlode@internal@naming:new(), gleam@result:'try'( begin _pipe = erlang:element(3, Cfg), _pipe@1 = gleam@list:try_map( _pipe, fun(_capture) -> generate_sql_block(Naming_ctx, _capture) end ), gleam@result:map(_pipe@1, fun lists:append/1) end, fun(Files) -> _pipe@2 = sqlode@internal@writer:write_all(Files), gleam@result:map_error( _pipe@2, fun(Field@0) -> {write_error, Field@0} end ) end ). -file("src/sqlode/internal/generate.gleam", 54). ?DOC(false). -spec run(binary()) -> {ok, list(binary())} | {error, generate_error()}. run(Config_path) -> gleam@result:'try'( begin _pipe = sqlode@internal@config:load(Config_path), gleam@result:map_error( _pipe, fun(Field@0) -> {config_error, Field@0} end ) end, fun(Cfg) -> Config_dir = filepath:directory_name(Config_path), Resolved = resolve_config_paths(Cfg, Config_dir), generate_config(Resolved) end ). -file("src/sqlode/internal/generate.gleam", 887). ?DOC(false). -spec error_to_string(generate_error()) -> binary(). error_to_string(Error) -> case Error of {config_error, Inner} -> sqlode@internal@config:error_to_string(Inner); {schema_read_error, Path, Detail} -> <<<>/binary, Detail/binary>>; {schema_parse_error, Detail@1} -> Detail@1; {query_read_error, Path@1, Detail@2} -> <<<>/binary, Detail@2/binary>>; {query_parse_error, _, Detail@3} -> Detail@3; {query_analysis_error, Detail@4} -> Detail@4; {no_queries_generated, Output, Parsed_query_count, Schema_table_count, Query_paths, Schema_paths} -> <<<<<<<<<<<<<<<<<<<<<<<<"No queries were generated for output directory: "/utf8, Output/binary>>/binary, "\n Parsed queries: "/utf8>>/binary, (erlang:integer_to_binary( Parsed_query_count ))/binary>>/binary, " (from "/utf8>>/binary, (gleam@string:join( Query_paths, <<", "/utf8>> ))/binary>>/binary, ")"/utf8>>/binary, "\n Schema tables: "/utf8>>/binary, (erlang:integer_to_binary(Schema_table_count))/binary>>/binary, " (from "/utf8>>/binary, (gleam@string:join(Schema_paths, <<", "/utf8>>))/binary>>/binary, ")"/utf8>>/binary, (case Parsed_query_count of 0 -> <<"\n Hint: No queries were found. Ensure your query files contain annotations like '-- name: QueryName :one'"/utf8>>; _ -> case Schema_table_count of 0 -> <<"\n Hint: No tables found in schema. Ensure your schema files contain CREATE TABLE statements"/utf8>>; _ -> <<"\n Hint: Queries were parsed but none produced output. Check that your schema defines the tables referenced in your queries"/utf8>> end end)/binary>>; {duplicate_query_name, Name, Paths} -> sqlode@internal@query_validation:error_to_string( {duplicate_name, Name, Paths} ); {normalized_query_name_collision, Function_name, Names, Paths@1} -> sqlode@internal@query_validation:error_to_string( {normalized_name_collision, Function_name, Names, Paths@1} ); {unsupported_annotation, Query_name, Command, Detail@5} -> sqlode@internal@query_validation:error_to_string( {unsupported_annotation, Query_name, Command, Detail@5} ); {invalid_out_path, Path@2} -> <<<<"Invalid output path \""/utf8, Path@2/binary>>/binary, "\": produces an invalid Gleam module path. Use a relative path under src/ (e.g., \"src/db\")"/utf8>>; {write_error, Inner@1} -> sqlode@internal@writer:error_to_string(Inner@1); vendor_runtime_not_found -> <<"vendor_runtime is enabled but the sqlode/runtime source file could not be found. Tried: src/sqlode/runtime.gleam, build/packages/sqlode/src/sqlode/runtime.gleam, build/dev/erlang/sqlode/src/sqlode/runtime.gleam"/utf8>>; {unsupported_array_for_engine, Query_name@1, Engine} -> sqlode@internal@query_validation:error_to_string( {unsupported_array_for_engine, Query_name@1, Engine} ) end.