Current section
Files
Jump to
Current section
Files
src/pgl/internal.gleam
import gleam/dict.{type Dict}
import gleam/function
import gleam/list
import gleam/option.{type Option}
import gleam/string
import neon/net
pub type Ssl {
SslDisabled
SslVerified
SslUnverified
}
pub const protocol_version_major = <<3:int-size(16)>>
pub const protocol_version_minor = <<0:int-size(16)>>
pub const default_host = "127.0.0.1"
pub const default_port = 5432
// ---------- Messages ---------- //
pub const header_size = 5
pub type Message {
AuthenticationOk
AuthenticationKerberosV5
AuthenticationCleartextPassword
AuthenticationMD5Password(salt: BitArray)
AuthenticationGSS
AuthenticationGSSContinue(data: BitArray)
AuthenticationSSPI
AuthenticationSCM
AuthenticationSASL(methods: List(String))
AuthenticationSASLContinue(server_first: BitArray)
AuthenticationSASLFinal(server_final: BitArray)
BackendKeyData(proc_id: Int, secret: Int)
BindComplete
CloseComplete
CommandComplete(command: Command, rows: Int)
CopyBothResponse
CopyData(data: BitArray)
CopyDone
CopyInResponse
CopyOutResponse
DataRow(values: List(Option(BitArray)))
EmptyQueryResponse
ErrorResponse(fields: Dict(BitArray, String))
FunctionCallResponse
NoData
NoticeResponse(fields: Dict(BitArray, String))
NotificationResponse(proc_id: Int, channel: BitArray, payload: BitArray)
ParameterDescription(count: Int, data_types: List(Int))
ParameterStatus(name: String, value: String)
ParseComplete
PortalSuspended
ReadyForQuery(status: Status)
RowDescription(count: Int, fields: List(RowDescriptionField))
}
pub type Status {
Idle
Transaction
Err
}
pub type PgSqlFormat {
Text
Binary
}
pub type RowDescriptionField {
RowDescriptionField(
name: String,
table_oid: Int,
attr_number: Int,
data_type_oid: Int,
data_type_size: Int,
type_modifier: Int,
format: PgSqlFormat,
)
}
pub type Command {
Select(Int)
Insert(Int)
Update(Int)
Delete(Int)
Fetch(Int)
Move(Int)
Copy(Int)
Begin
Commit
Rollback
Other(String)
// verb
}
// ---------- Errors ---------- //
pub type InternalError {
AuthenticationError(kind: AuthenticationError, message: String)
SocketError(kind: SocketError, message: String)
ProtocolError(kind: ProtocolError, message: String)
PostgresError(
code: String,
name: String,
message: String,
fields: Dict(BitArray, String),
)
}
pub type SocketError {
Closed
Timeout
SystemLimit
Posix(net.Posix)
TcpError(String)
TlsAlert(String)
SslSockError(String)
ConnectError(String)
}
pub fn socket_error_to_string(err: SocketError) -> String {
case err {
Closed -> "Closed"
Timeout -> "Timeout"
SystemLimit -> "SystemLimit"
Posix(code) -> net.posix_to_string(code)
TcpError(message) -> "TcpError(" <> message <> ")"
TlsAlert(message) -> "TlsAlert(" <> message <> ")"
SslSockError(message) -> "SslError(" <> message <> ")"
ConnectError(message) -> "ConnectError(" <> message <> ")"
}
}
pub fn error_to_string(err: InternalError) -> String {
case err {
AuthenticationError(kind, msg) -> {
let name = "AuthenticationError[" <> auth_error_to_string(kind) <> "]"
format_error(name, msg)
}
SocketError(kind, msg) -> {
let name = "SocketError[" <> socket_error_to_string(kind) <> "]"
format_error(name, msg)
}
ProtocolError(kind, msg) -> {
let name = "ProtocolError[" <> protocol_error_to_string(kind) <> "]"
format_error(name, msg)
}
PostgresError(code, name, message, _) ->
format_error_with_values(
"PostgresError",
"",
[#("code", code), #("name", name), #("message", message)],
function.identity,
)
}
}
pub fn format_error(name: String, message: String) -> String {
format_error_with_values(name, message, [], function.identity)
}
pub fn format_error_with_values(
name: String,
message: String,
key_vals: List(#(String, a)),
value_formatter: fn(a) -> String,
) -> String {
let format_key_vals = fn() {
key_vals
|> list.map(fn(key_val) {
let #(key, val) = key_val
key <> ": " <> value_formatter(val)
})
}
case name, message {
"", "" -> []
name, "" -> ["(" <> name <> ")", ..format_key_vals()]
name, message -> ["(" <> name <> ") " <> message, ..format_key_vals()]
}
|> string.join(", ")
}
pub fn auth_error_to_string(err: AuthenticationError) -> String {
case err {
AuthenticationFailed -> "AuthenticationFailed"
MethodNotImplemented -> "MethodNotImplemented"
}
}
pub type AuthenticationError {
AuthenticationFailed
MethodNotImplemented
}
pub type ProtocolError {
SaslClientFirst
SaslClientFinal
SaslServerError
SaslServerFinal
SaslServerFirst
ProcessingError
EncodingError
DecodingError
MessageError
SslError
}
pub fn protocol_error_to_string(err: ProtocolError) {
case err {
SaslClientFirst -> "SaslClientFirst"
SaslClientFinal -> "SaslClientFinal"
SaslServerError -> "SaslServerError"
SaslServerFinal -> "SaslServerFinal"
SaslServerFirst -> "SaslServerFirst"
ProcessingError -> "ProcessingError"
EncodingError -> "EncodingError"
DecodingError -> "DecodingError"
MessageError -> "MessageError"
SslError -> "SSLError"
}
}
/// https://www.postgresql.org/docs/current/errcodes-appendix.html
pub fn pg_error_code_name(error_code: String) -> Result(String, Nil) {
case error_code {
"00000" -> Ok("successful_completion")
"01000" -> Ok("warning")
"0100C" -> Ok("dynamic_result_sets_returned")
"01008" -> Ok("implicit_zero_bit_padding")
"01003" -> Ok("null_value_eliminated_in_set_function")
"01007" -> Ok("privilege_not_granted")
"01006" -> Ok("privilege_not_revoked")
"01004" -> Ok("string_data_right_truncation")
"01P01" -> Ok("deprecated_feature")
"02000" -> Ok("no_data")
"02001" -> Ok("no_additional_dynamic_result_sets_returned")
"03000" -> Ok("sql_statement_not_yet_complete")
"08000" -> Ok("connection_exception")
"08003" -> Ok("connection_does_not_exist")
"08006" -> Ok("connection_failure")
"08001" -> Ok("sqlclient_unable_to_establish_sqlconnection")
"08004" -> Ok("sqlserver_rejected_establishment_of_sqlconnection")
"08007" -> Ok("transaction_resolution_unknown")
"08P01" -> Ok("protocol_violation")
"09000" -> Ok("triggered_action_exception")
"0A000" -> Ok("feature_not_supported")
"0B000" -> Ok("invalid_transaction_initiation")
"0F000" -> Ok("locator_exception")
"0F001" -> Ok("invalid_locator_specification")
"0L000" -> Ok("invalid_grantor")
"0LP01" -> Ok("invalid_grant_operation")
"0P000" -> Ok("invalid_role_specification")
"0Z000" -> Ok("diagnostics_exception")
"0Z002" -> Ok("stacked_diagnostics_accessed_without_active_query")
"20000" -> Ok("case_not_found")
"21000" -> Ok("cardinality_violation")
"22000" -> Ok("data_exception")
"2202E" -> Ok("array_subscript_error")
"22021" -> Ok("character_not_in_repertoire")
"22008" -> Ok("datetime_field_overflow")
"22012" -> Ok("division_by_zero")
"22005" -> Ok("error_in_assignment")
"2200B" -> Ok("escape_character_conflict")
"22022" -> Ok("indicator_overflow")
"22015" -> Ok("interval_field_overflow")
"2201E" -> Ok("invalid_argument_for_logarithm")
"22014" -> Ok("invalid_argument_for_ntile_function")
"22016" -> Ok("invalid_argument_for_nth_value_function")
"2201F" -> Ok("invalid_argument_for_power_function")
"2201G" -> Ok("invalid_argument_for_width_bucket_function")
"22018" -> Ok("invalid_character_value_for_cast")
"22007" -> Ok("invalid_datetime_format")
"22019" -> Ok("invalid_escape_character")
"2200D" -> Ok("invalid_escape_octet")
"22025" -> Ok("invalid_escape_sequence")
"22P06" -> Ok("nonstandard_use_of_escape_character")
"22010" -> Ok("invalid_indicator_parameter_value")
"22023" -> Ok("invalid_parameter_value")
"22013" -> Ok("invalid_preceding_or_following_size")
"2201B" -> Ok("invalid_regular_expression")
"2201W" -> Ok("invalid_row_count_in_limit_clause")
"2201X" -> Ok("invalid_row_count_in_result_offset_clause")
"2202H" -> Ok("invalid_tablesample_argument")
"2202G" -> Ok("invalid_tablesample_repeat")
"22009" -> Ok("invalid_time_zone_displacement_value")
"2200C" -> Ok("invalid_use_of_escape_character")
"2200G" -> Ok("most_specific_type_mismatch")
"22004" -> Ok("null_value_not_allowed")
"22002" -> Ok("null_value_no_indicator_parameter")
"22003" -> Ok("numeric_value_out_of_range")
"2200H" -> Ok("sequence_generator_limit_exceeded")
"22026" -> Ok("string_data_length_mismatch")
"22001" -> Ok("string_data_right_truncation")
"22011" -> Ok("substring_error")
"22027" -> Ok("trim_error")
"22024" -> Ok("unterminated_c_string")
"2200F" -> Ok("zero_length_character_string")
"22P01" -> Ok("floating_point_exception")
"22P02" -> Ok("invalid_text_representation")
"22P03" -> Ok("invalid_binary_representation")
"22P04" -> Ok("bad_copy_file_format")
"22P05" -> Ok("untranslatable_character")
"2200L" -> Ok("not_an_xml_document")
"2200M" -> Ok("invalid_xml_document")
"2200N" -> Ok("invalid_xml_content")
"2200S" -> Ok("invalid_xml_comment")
"2200T" -> Ok("invalid_xml_processing_instruction")
"22030" -> Ok("duplicate_json_object_key_value")
"22031" -> Ok("invalid_argument_for_sql_json_datetime_function")
"22032" -> Ok("invalid_json_text")
"22033" -> Ok("invalid_sql_json_subscript")
"22034" -> Ok("more_than_one_sql_json_item")
"22035" -> Ok("no_sql_json_item")
"22036" -> Ok("non_numeric_sql_json_item")
"22037" -> Ok("non_unique_keys_in_a_json_object")
"22038" -> Ok("singleton_sql_json_item_required")
"22039" -> Ok("sql_json_array_not_found")
"2203A" -> Ok("sql_json_member_not_found")
"2203B" -> Ok("sql_json_number_not_found")
"2203C" -> Ok("sql_json_object_not_found")
"2203D" -> Ok("too_many_json_array_elements")
"2203E" -> Ok("too_many_json_object_members")
"2203F" -> Ok("sql_json_scalar_required")
"23000" -> Ok("integrity_constraint_violation")
"23001" -> Ok("restrict_violation")
"23502" -> Ok("not_null_violation")
"23503" -> Ok("foreign_key_violation")
"23505" -> Ok("unique_violation")
"23514" -> Ok("check_violation")
"23P01" -> Ok("exclusion_violation")
"24000" -> Ok("invalid_cursor_state")
"25000" -> Ok("invalid_transaction_state")
"25001" -> Ok("active_sql_transaction")
"25002" -> Ok("branch_transaction_already_active")
"25008" -> Ok("held_cursor_requires_same_isolation_level")
"25003" -> Ok("inappropriate_access_mode_for_branch_transaction")
"25004" -> Ok("inappropriate_isolation_level_for_branch_transaction")
"25005" -> Ok("no_active_sql_transaction_for_branch_transaction")
"25006" -> Ok("read_only_sql_transaction")
"25007" -> Ok("schema_and_data_statement_mixing_not_supported")
"25P01" -> Ok("no_active_sql_transaction")
"25P02" -> Ok("in_failed_sql_transaction")
"25P03" -> Ok("idle_in_transaction_session_timeout")
"26000" -> Ok("invalid_sql_statement_name")
"27000" -> Ok("triggered_data_change_violation")
"28000" -> Ok("invalid_authorization_specification")
"28P01" -> Ok("invalid_password")
"2B000" -> Ok("dependent_privilege_descriptors_still_exist")
"2BP01" -> Ok("dependent_objects_still_exist")
"2D000" -> Ok("invalid_transaction_termination")
"2F000" -> Ok("sql_routine_exception")
"2F005" -> Ok("function_executed_no_return_statement")
"2F002" -> Ok("modifying_sql_data_not_permitted")
"2F003" -> Ok("prohibited_sql_statement_attempted")
"2F004" -> Ok("reading_sql_data_not_permitted")
"34000" -> Ok("invalid_cursor_name")
"38000" -> Ok("external_routine_exception")
"38001" -> Ok("containing_sql_not_permitted")
"38002" -> Ok("modifying_sql_data_not_permitted")
"38003" -> Ok("prohibited_sql_statement_attempted")
"38004" -> Ok("reading_sql_data_not_permitted")
"39000" -> Ok("external_routine_invocation_exception")
"39001" -> Ok("invalid_sqlstate_returned")
"39004" -> Ok("null_value_not_allowed")
"39P01" -> Ok("trigger_protocol_violated")
"39P02" -> Ok("srf_protocol_violated")
"39P03" -> Ok("event_trigger_protocol_violated")
"3B000" -> Ok("savepoint_exception")
"3B001" -> Ok("invalid_savepoint_specification")
"3D000" -> Ok("invalid_catalog_name")
"3F000" -> Ok("invalid_schema_name")
"40000" -> Ok("transaction_rollback")
"40002" -> Ok("transaction_integrity_constraint_violation")
"40001" -> Ok("serialization_failure")
"40003" -> Ok("statement_completion_unknown")
"40P01" -> Ok("deadlock_detected")
"42000" -> Ok("syntax_error_or_access_rule_violation")
"42601" -> Ok("syntax_error")
"42501" -> Ok("insufficient_privilege")
"42846" -> Ok("cannot_coerce")
"42803" -> Ok("grouping_error")
"42P20" -> Ok("windowing_error")
"42P19" -> Ok("invalid_recursion")
"42830" -> Ok("invalid_foreign_key")
"42602" -> Ok("invalid_name")
"42622" -> Ok("name_too_long")
"42939" -> Ok("reserved_name")
"42804" -> Ok("datatype_mismatch")
"42P18" -> Ok("indeterminate_datatype")
"42P21" -> Ok("collation_mismatch")
"42P22" -> Ok("indeterminate_collation")
"42809" -> Ok("wrong_object_type")
"428C9" -> Ok("generated_always")
"42703" -> Ok("undefined_column")
"42883" -> Ok("undefined_function")
"42P01" -> Ok("undefined_table")
"42P02" -> Ok("undefined_parameter")
"42704" -> Ok("undefined_object")
"42701" -> Ok("duplicate_column")
"42P03" -> Ok("duplicate_cursor")
"42P04" -> Ok("duplicate_database")
"42723" -> Ok("duplicate_function")
"42P05" -> Ok("duplicate_prepared_statement")
"42P06" -> Ok("duplicate_schema")
"42P07" -> Ok("duplicate_table")
"42712" -> Ok("duplicate_alias")
"42710" -> Ok("duplicate_object")
"42702" -> Ok("ambiguous_column")
"42725" -> Ok("ambiguous_function")
"42P08" -> Ok("ambiguous_parameter")
"42P09" -> Ok("ambiguous_alias")
"42P10" -> Ok("invalid_column_reference")
"42611" -> Ok("invalid_column_definition")
"42P11" -> Ok("invalid_cursor_definition")
"42P12" -> Ok("invalid_database_definition")
"42P13" -> Ok("invalid_function_definition")
"42P14" -> Ok("invalid_prepared_statement_definition")
"42P15" -> Ok("invalid_schema_definition")
"42P16" -> Ok("invalid_table_definition")
"42P17" -> Ok("invalid_object_definition")
"44000" -> Ok("with_check_option_violation")
"53000" -> Ok("insufficient_resources")
"53100" -> Ok("disk_full")
"53200" -> Ok("out_of_memory")
"53300" -> Ok("too_many_connections")
"53400" -> Ok("configuration_limit_exceeded")
"54000" -> Ok("program_limit_exceeded")
"54001" -> Ok("statement_too_complex")
"54011" -> Ok("too_many_columns")
"54023" -> Ok("too_many_arguments")
"55000" -> Ok("object_not_in_prerequisite_state")
"55006" -> Ok("object_in_use")
"55P02" -> Ok("cant_change_runtime_param")
"55P03" -> Ok("lock_not_available")
"55P04" -> Ok("unsafe_new_enum_value_usage")
"57000" -> Ok("operator_intervention")
"57014" -> Ok("query_canceled")
"57P01" -> Ok("admin_shutdown")
"57P02" -> Ok("crash_shutdown")
"57P03" -> Ok("cannot_connect_now")
"57P04" -> Ok("database_dropped")
"57P05" -> Ok("idle_session_timeout")
"58000" -> Ok("system_error")
"58030" -> Ok("io_error")
"58P01" -> Ok("undefined_file")
"58P02" -> Ok("duplicate_file")
"72000" -> Ok("snapshot_too_old")
"F0000" -> Ok("config_file_error")
"F0001" -> Ok("lock_file_exists")
"HV000" -> Ok("fdw_error")
"HV005" -> Ok("fdw_column_name_not_found")
"HV002" -> Ok("fdw_dynamic_parameter_value_needed")
"HV010" -> Ok("fdw_function_sequence_error")
"HV021" -> Ok("fdw_inconsistent_descriptor_information")
"HV024" -> Ok("fdw_invalid_attribute_value")
"HV007" -> Ok("fdw_invalid_column_name")
"HV008" -> Ok("fdw_invalid_column_number")
"HV004" -> Ok("fdw_invalid_data_type")
"HV006" -> Ok("fdw_invalid_data_type_descriptors")
"HV091" -> Ok("fdw_invalid_descriptor_field_identifier")
"HV00B" -> Ok("fdw_invalid_handle")
"HV00C" -> Ok("fdw_invalid_option_index")
"HV00D" -> Ok("fdw_invalid_option_name")
"HV090" -> Ok("fdw_invalid_string_length_or_buffer_length")
"HV00A" -> Ok("fdw_invalid_string_format")
"HV009" -> Ok("fdw_invalid_use_of_null_pointer")
"HV014" -> Ok("fdw_too_many_handles")
"HV001" -> Ok("fdw_out_of_memory")
"HV00P" -> Ok("fdw_no_schemas")
"HV00J" -> Ok("fdw_option_name_not_found")
"HV00K" -> Ok("fdw_reply_handle")
"HV00Q" -> Ok("fdw_schema_not_found")
"HV00R" -> Ok("fdw_table_not_found")
"HV00L" -> Ok("fdw_unable_to_create_execution")
"HV00M" -> Ok("fdw_unable_to_create_reply")
"HV00N" -> Ok("fdw_unable_to_establish_connection")
"P0000" -> Ok("plpgsql_error")
"P0001" -> Ok("raise_exception")
"P0002" -> Ok("no_data_found")
"P0003" -> Ok("too_many_rows")
"P0004" -> Ok("assert_failure")
"XX000" -> Ok("internal_error")
"XX001" -> Ok("data_corrupted")
"XX002" -> Ok("index_corrupted")
_ -> Error(Nil)
}
}
// ---------- Exceptions ---------- //
@external(erlang, "pgl_ffi", "rescue")
pub fn with_rescue(next: fn() -> t) -> Result(t, Nil)
@external(erlang, "pgl_ffi", "handle_crash")
pub fn on_crash(handler: fn() -> a, next: fn() -> b) -> b
pub fn assert_on_crash(
handler: fn() -> Result(a, err),
message: String,
next: fn() -> b,
) -> b {
let handler = fn() {
let message = message <> " failed!"
let assert Ok(_) = handler() as message
}
on_crash(handler, next)
}