Packages

A compile-time safe database library for Gleam - Ecto-inspired schemas, composable queries, and adapter-based persistence

Current section

Files

Jump to
cquill src cquill@error.erl
Raw

src/cquill@error.erl

-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}.