-module(cquill@error). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/cquill/error.gleam"). -export([is_not_found/1, is_constraint_violation/1, is_unique_violation/1, is_foreign_key_violation/1, is_connection_error/1, is_recoverable/1, is_query_error/1, format_error/1, format_transaction_error/1, format_savepoint_error/1, from_postgres_error/3, from_mysql_error/2, from_sqlite_error/2, not_found/0, query_failed/1, query_failed_with_code/2, connection_failed/1, timeout/0, unique_violation/2, not_supported/1, adapter_specific/2]). -export_type([adapter_error/0, transaction_error/1, savepoint_error/1]). -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 adapter_error() :: not_found | {too_many_rows, integer(), integer()} | {connection_failed, binary()} | connection_timeout | pool_exhausted | {connection_lost, binary()} | {query_failed, binary(), gleam@option:option(binary())} | {decode_failed, integer(), binary(), binary(), binary()} | timeout | {unique_violation, binary(), binary()} | {foreign_key_violation, binary(), binary()} | {check_violation, binary(), binary()} | {not_null_violation, binary()} | {constraint_violation, binary(), binary()} | {stale_data, binary(), binary()} | {data_integrity_error, binary()} | {not_supported, binary()} | {adapter_specific, binary(), binary()}. -type transaction_error(LAZ) :: {user_error, LAZ} | {adapter_transaction_error, adapter_error()} | {begin_failed, binary()} | {commit_failed, binary()} | rolled_back | {transaction_rollback, binary()} | transaction_connection_lost | nested_transaction_error | transaction_timeout | serialization_failure. -type savepoint_error(LBA) :: {savepoint_not_found, binary()} | {savepoint_adapter_error, adapter_error()} | {savepoint_user_error, LBA} | {savepoint_creation_failed, binary()} | {savepoint_release_failed, binary()} | savepoint_no_transaction. -file("src/cquill/error.gleam", 166). ?DOC(" Check if an error indicates the record was not found\n"). -spec is_not_found(adapter_error()) -> boolean(). is_not_found(Error) -> case Error of not_found -> true; _ -> false end. -file("src/cquill/error.gleam", 174). ?DOC(" Check if an error is any kind of constraint violation\n"). -spec is_constraint_violation(adapter_error()) -> boolean(). is_constraint_violation(Error) -> case Error of {unique_violation, _, _} -> true; {foreign_key_violation, _, _} -> true; {check_violation, _, _} -> true; {not_null_violation, _} -> true; {constraint_violation, _, _} -> true; _ -> false end. -file("src/cquill/error.gleam", 186). ?DOC(" Check if an error is a unique constraint violation\n"). -spec is_unique_violation(adapter_error()) -> boolean(). is_unique_violation(Error) -> case Error of {unique_violation, _, _} -> true; _ -> false end. -file("src/cquill/error.gleam", 194). ?DOC(" Check if an error is a foreign key violation\n"). -spec is_foreign_key_violation(adapter_error()) -> boolean(). is_foreign_key_violation(Error) -> case Error of {foreign_key_violation, _, _} -> true; _ -> false end. -file("src/cquill/error.gleam", 202). ?DOC(" Check if an error is a connection-related error\n"). -spec is_connection_error(adapter_error()) -> boolean(). is_connection_error(Error) -> case Error of {connection_failed, _} -> true; connection_timeout -> true; pool_exhausted -> true; {connection_lost, _} -> true; _ -> false end. -file("src/cquill/error.gleam", 213). ?DOC(" Check if an error is recoverable (can retry)\n"). -spec is_recoverable(adapter_error()) -> boolean(). is_recoverable(Error) -> case Error of {connection_failed, _} -> true; connection_timeout -> true; pool_exhausted -> true; {connection_lost, _} -> true; timeout -> true; {stale_data, _, _} -> true; _ -> false end. -file("src/cquill/error.gleam", 230). ?DOC(" Check if an error is a query/execution error\n"). -spec is_query_error(adapter_error()) -> boolean(). is_query_error(Error) -> case Error of {query_failed, _, _} -> true; {decode_failed, _, _, _, _} -> true; timeout -> true; _ -> false end. -file("src/cquill/error.gleam", 244). ?DOC(" Format an error for display\n"). -spec format_error(adapter_error()) -> binary(). format_error(Error) -> case Error of not_found -> <<"Record not found"/utf8>>; {too_many_rows, Expected, Got} -> <<<<<<"Too many rows: expected "/utf8, (gleam@string:inspect(Expected))/binary>>/binary, ", got "/utf8>>/binary, (gleam@string:inspect(Got))/binary>>; {connection_failed, Reason} -> <<"Connection failed: "/utf8, Reason/binary>>; connection_timeout -> <<"Connection timed out"/utf8>>; pool_exhausted -> <<"Connection pool exhausted"/utf8>>; {connection_lost, Reason@1} -> <<"Connection lost: "/utf8, Reason@1/binary>>; {query_failed, Message, none} -> <<"Query failed: "/utf8, Message/binary>>; {query_failed, Message@1, {some, Code}} -> <<<<<<"Query failed ["/utf8, Code/binary>>/binary, "]: "/utf8>>/binary, Message@1/binary>>; {decode_failed, Row, Column, Expected@1, Got@1} -> <<<<<<<<<<<<<<"Decode failed at row "/utf8, (gleam@string:inspect(Row))/binary>>/binary, ", column "/utf8>>/binary, Column/binary>>/binary, ": expected "/utf8>>/binary, Expected@1/binary>>/binary, ", got "/utf8>>/binary, Got@1/binary>>; timeout -> <<"Operation timed out"/utf8>>; {unique_violation, Constraint, Detail} -> <<<<<<"Unique constraint violation: "/utf8, Constraint/binary>>/binary, " - "/utf8>>/binary, Detail/binary>>; {foreign_key_violation, Constraint@1, Detail@1} -> <<<<<<"Foreign key violation: "/utf8, Constraint@1/binary>>/binary, " - "/utf8>>/binary, Detail@1/binary>>; {check_violation, Constraint@2, Detail@2} -> <<<<<<"Check constraint violation: "/utf8, Constraint@2/binary>>/binary, " - "/utf8>>/binary, Detail@2/binary>>; {not_null_violation, Column@1} -> <<"NOT NULL violation on column: "/utf8, Column@1/binary>>; {constraint_violation, Constraint@3, Detail@3} -> <<<<<<"Constraint violation: "/utf8, Constraint@3/binary>>/binary, " - "/utf8>>/binary, Detail@3/binary>>; {stale_data, Expected@2, Actual} -> <<<<<<"Stale data: expected version "/utf8, Expected@2/binary>>/binary, ", found "/utf8>>/binary, Actual/binary>>; {data_integrity_error, Message@2} -> <<"Data integrity error: "/utf8, Message@2/binary>>; {not_supported, Operation} -> <<"Operation not supported: "/utf8, Operation/binary>>; {adapter_specific, Code@1, Message@3} -> <<<<<<"Adapter error ["/utf8, Code@1/binary>>/binary, "]: "/utf8>>/binary, Message@3/binary>> end. -file("src/cquill/error.gleam", 294). ?DOC(" Format a transaction error for display\n"). -spec format_transaction_error(transaction_error(any())) -> binary(). format_transaction_error(Error) -> case Error of {user_error, _} -> <<"Transaction aborted: user error"/utf8>>; {adapter_transaction_error, Adapter_err} -> <<"Transaction aborted: "/utf8, (format_error(Adapter_err))/binary>>; {begin_failed, Reason} -> <<"Failed to begin transaction: "/utf8, Reason/binary>>; {commit_failed, Reason@1} -> <<"Failed to commit transaction: "/utf8, Reason@1/binary>>; rolled_back -> <<"Transaction was rolled back"/utf8>>; {transaction_rollback, Reason@2} -> <<"Transaction rolled back: "/utf8, Reason@2/binary>>; transaction_connection_lost -> <<"Connection lost during transaction"/utf8>>; nested_transaction_error -> <<"Nested transactions are not supported"/utf8>>; transaction_timeout -> <<"Transaction timed out"/utf8>>; serialization_failure -> <<"Serialization failure: concurrent transaction conflict (retry may succeed)"/utf8>> end. -file("src/cquill/error.gleam", 312). ?DOC(" Format a savepoint error for display\n"). -spec format_savepoint_error(savepoint_error(any())) -> binary(). format_savepoint_error(Error) -> case Error of {savepoint_not_found, Name} -> <<"Savepoint not found: "/utf8, Name/binary>>; {savepoint_adapter_error, Adapter_err} -> <<"Savepoint operation failed: "/utf8, (format_error(Adapter_err))/binary>>; {savepoint_user_error, _} -> <<"Savepoint aborted: user error"/utf8>>; {savepoint_creation_failed, Reason} -> <<"Failed to create savepoint: "/utf8, Reason/binary>>; {savepoint_release_failed, Reason@1} -> <<"Failed to release savepoint: "/utf8, Reason@1/binary>>; savepoint_no_transaction -> <<"Cannot use savepoint outside of a transaction"/utf8>> end. -file("src/cquill/error.gleam", 486). ?DOC(" Extract constraint name from error detail/message\n"). -spec extract_constraint_name(binary()) -> binary(). extract_constraint_name(Text) -> case gleam@string:split(Text, <<"constraint \""/utf8>>) of [_, Rest] -> case gleam@string:split(Rest, <<"\""/utf8>>) of [Name | _] -> Name; _ -> <<"unknown"/utf8>> end; _ -> case gleam@string:split(Text, <<"for key '"/utf8>>) of [_, Rest@1] -> case gleam@string:split(Rest@1, <<"'"/utf8>>) of [Name@1 | _] -> Name@1; _ -> <<"unknown"/utf8>> end; _ -> <<"unknown"/utf8>> end end. -file("src/cquill/error.gleam", 510). ?DOC(" Extract column name from error message\n"). -spec extract_column_name(binary()) -> binary(). extract_column_name(Text) -> case gleam@string:split(Text, <<"column \""/utf8>>) of [_, Rest] -> case gleam@string:split(Rest, <<"\""/utf8>>) of [Name | _] -> Name; _ -> <<"unknown"/utf8>> end; _ -> case gleam@string:split(Text, <<"Field '"/utf8>>) of [_, Rest@1] -> case gleam@string:split(Rest@1, <<"'"/utf8>>) of [Name@1 | _] -> Name@1; _ -> <<"unknown"/utf8>> end; _ -> <<"unknown"/utf8>> end end. -file("src/cquill/error.gleam", 346). ?DOC( " Map a PostgreSQL error code to an AdapterError.\n" "\n" " PostgreSQL error codes are 5-character strings where the first two\n" " characters indicate the error class. See:\n" " https://www.postgresql.org/docs/current/errcodes-appendix.html\n" "\n" " Common codes:\n" " - 23505: unique_violation\n" " - 23503: foreign_key_violation\n" " - 23514: check_violation\n" " - 23502: not_null_violation\n" " - 23000: integrity_constraint_violation\n" " - 08000: connection_exception\n" " - 08003: connection_does_not_exist\n" " - 08006: connection_failure\n" " - 57P01: admin_shutdown\n" " - 42P01: undefined_table\n" " - 42703: undefined_column\n" ). -spec from_postgres_error(binary(), binary(), binary()) -> adapter_error(). from_postgres_error(Code, Message, Detail) -> case Code of <<"23505"/utf8>> -> {unique_violation, extract_constraint_name(Detail), Detail}; <<"23503"/utf8>> -> {foreign_key_violation, extract_constraint_name(Detail), Detail}; <<"23514"/utf8>> -> {check_violation, extract_constraint_name(Detail), Detail}; <<"23502"/utf8>> -> {not_null_violation, extract_column_name(Message)}; <<"23000"/utf8>> -> {constraint_violation, extract_constraint_name(Detail), Detail}; <<"08000"/utf8>> -> {connection_failed, Message}; <<"08003"/utf8>> -> {connection_lost, <<"Connection does not exist"/utf8>>}; <<"08006"/utf8>> -> {connection_failed, <<"Connection failure: "/utf8, Message/binary>>}; <<"57P01"/utf8>> -> {connection_lost, <<"Server shutdown"/utf8>>}; <<"57P02"/utf8>> -> {connection_lost, <<"Crash shutdown"/utf8>>}; <<"57P03"/utf8>> -> {connection_failed, <<"Cannot connect now"/utf8>>}; <<"3D000"/utf8>> -> {query_failed, Message, {some, Code}}; <<"3F000"/utf8>> -> {query_failed, Message, {some, Code}}; <<"42P01"/utf8>> -> {query_failed, <<"Undefined table: "/utf8, Message/binary>>, {some, Code}}; <<"42703"/utf8>> -> {query_failed, <<"Undefined column: "/utf8, Message/binary>>, {some, Code}}; <<"42601"/utf8>> -> {query_failed, <<"Syntax error: "/utf8, Message/binary>>, {some, Code}}; <<"42501"/utf8>> -> {query_failed, <<"Insufficient privilege: "/utf8, Message/binary>>, {some, Code}}; <<"57014"/utf8>> -> timeout; <<"53300"/utf8>> -> pool_exhausted; <<"53400"/utf8>> -> {query_failed, <<"Configuration limit exceeded: "/utf8, Message/binary>>, {some, Code}}; _ -> {adapter_specific, Code, Message} end. -file("src/cquill/error.gleam", 405). ?DOC( " Map a MySQL error code to an AdapterError.\n" "\n" " Common MySQL error codes:\n" " - 1062: Duplicate entry (unique violation)\n" " - 1452: Foreign key constraint fails\n" " - 1364: No default value (not null violation)\n" " - 1048: Column cannot be null\n" " - 2002: Connection refused\n" " - 2003: Can't connect to server\n" " - 2006: Server has gone away\n" " - 2013: Lost connection during query\n" ). -spec from_mysql_error(integer(), binary()) -> adapter_error(). from_mysql_error(Code, Message) -> case Code of 1062 -> {unique_violation, extract_constraint_name(Message), Message}; 1452 -> {foreign_key_violation, extract_constraint_name(Message), Message}; 1364 -> {not_null_violation, extract_column_name(Message)}; 1048 -> {not_null_violation, extract_column_name(Message)}; 3819 -> {check_violation, extract_constraint_name(Message), Message}; 2002 -> {connection_failed, <<"Connection refused"/utf8>>}; 2003 -> {connection_failed, <<"Can't connect to server"/utf8>>}; 2006 -> {connection_lost, <<"Server has gone away"/utf8>>}; 2013 -> {connection_lost, <<"Lost connection during query"/utf8>>}; 1064 -> {query_failed, <<"Syntax error: "/utf8, Message/binary>>, {some, gleam@string:inspect(Code)}}; 1146 -> {query_failed, <<"Table doesn't exist: "/utf8, Message/binary>>, {some, gleam@string:inspect(Code)}}; 1054 -> {query_failed, <<"Unknown column: "/utf8, Message/binary>>, {some, gleam@string:inspect(Code)}}; _ -> {adapter_specific, gleam@string:inspect(Code), Message} end. -file("src/cquill/error.gleam", 439). ?DOC( " Map a SQLite error code to an AdapterError.\n" "\n" " SQLite uses integer result codes. Extended result codes provide more detail.\n" " See: https://www.sqlite.org/rescode.html\n" ). -spec from_sqlite_error(integer(), binary()) -> adapter_error(). from_sqlite_error(Code, Message) -> case Code of 19 -> case gleam_stdlib:contains_string( string:lowercase(Message), <<"unique"/utf8>> ) of true -> {unique_violation, extract_constraint_name(Message), Message}; false -> case gleam_stdlib:contains_string( string:lowercase(Message), <<"foreign key"/utf8>> ) of true -> {foreign_key_violation, extract_constraint_name(Message), Message}; false -> case gleam_stdlib:contains_string( string:lowercase(Message), <<"not null"/utf8>> ) of true -> {not_null_violation, extract_column_name(Message)}; false -> case gleam_stdlib:contains_string( string:lowercase(Message), <<"check"/utf8>> ) of true -> {check_violation, extract_constraint_name(Message), Message}; false -> {constraint_violation, <<"unknown"/utf8>>, Message} end end end end; 5 -> {connection_failed, <<"Database is locked"/utf8>>}; 6 -> {connection_failed, <<"Table is locked"/utf8>>}; 26 -> {connection_failed, <<"Not a database file"/utf8>>}; 1 -> {query_failed, Message, {some, gleam@string:inspect(Code)}}; _ -> {adapter_specific, gleam@string:inspect(Code), Message} end. -file("src/cquill/error.gleam", 537). ?DOC(" Create a NotFound error\n"). -spec not_found() -> adapter_error(). not_found() -> not_found. -file("src/cquill/error.gleam", 542). ?DOC(" Create a QueryFailed error\n"). -spec query_failed(binary()) -> adapter_error(). query_failed(Message) -> {query_failed, Message, none}. -file("src/cquill/error.gleam", 547). ?DOC(" Create a QueryFailed error with code\n"). -spec query_failed_with_code(binary(), binary()) -> adapter_error(). query_failed_with_code(Message, Code) -> {query_failed, Message, {some, Code}}. -file("src/cquill/error.gleam", 552). ?DOC(" Create a ConnectionFailed error\n"). -spec connection_failed(binary()) -> adapter_error(). connection_failed(Reason) -> {connection_failed, Reason}. -file("src/cquill/error.gleam", 557). ?DOC(" Create a Timeout error\n"). -spec timeout() -> adapter_error(). timeout() -> timeout. -file("src/cquill/error.gleam", 562). ?DOC(" Create a UniqueViolation error\n"). -spec unique_violation(binary(), binary()) -> adapter_error(). unique_violation(Constraint, Detail) -> {unique_violation, Constraint, Detail}. -file("src/cquill/error.gleam", 567). ?DOC(" Create a NotSupported error\n"). -spec not_supported(binary()) -> adapter_error(). not_supported(Operation) -> {not_supported, Operation}. -file("src/cquill/error.gleam", 572). ?DOC(" Create an AdapterSpecific error\n"). -spec adapter_specific(binary(), binary()) -> adapter_error(). adapter_specific(Code, Message) -> {adapter_specific, Code, Message}.