@@ -29,6 +29,298 @@ pub fn decodeInternal(this: *@This(), comptime Container: type, reader: NewReade
2929
3030pub const decode = DecoderWrap (ErrorResponse , decodeInternal ).decode ;
3131
32+ const ERROR_CODE_MAP = std .StaticStringMap ([]const u8 ).initComptime (.{
33+ .{ "00000" , "successful_completion" },
34+ .{ "01000" , "warning" },
35+ .{ "0100C" , "dynamic_result_sets_returned" },
36+ .{ "01008" , "implicit_zero_bit_padding" },
37+ .{ "01003" , "null_value_eliminated_in_set_function" },
38+ .{ "01007" , "privilege_not_granted" },
39+ .{ "01006" , "privilege_not_revoked" },
40+ .{ "01004" , "string_data_right_truncation" },
41+ .{ "01P01" , "deprecated_feature" },
42+ .{ "02000" , "no_data" },
43+ .{ "02001" , "no_additional_dynamic_result_sets_returned" },
44+ .{ "03000" , "sql_statement_not_yet_complete" },
45+ .{ "08000" , "connection_exception" },
46+ .{ "08003" , "connection_does_not_exist" },
47+ .{ "08006" , "connection_failure" },
48+ .{ "08001" , "sqlclient_unable_to_establish_sqlconnection" },
49+ .{ "08004" , "sqlserver_rejected_establishment_of_sqlconnection" },
50+ .{ "08007" , "transaction_resolution_unknown" },
51+ .{ "08P01" , "protocol_violation" },
52+ .{ "09000" , "triggered_action_exception" },
53+ .{ "0A000" , "feature_not_supported" },
54+ .{ "0B000" , "invalid_transaction_initiation" },
55+ .{ "0F000" , "locator_exception" },
56+ .{ "0F001" , "invalid_locator_specification" },
57+ .{ "0L000" , "invalid_grantor" },
58+ .{ "0LP01" , "invalid_grant_operation" },
59+ .{ "0P000" , "invalid_role_specification" },
60+ .{ "0Z000" , "diagnostics_exception" },
61+ .{ "0Z002" , "stacked_diagnostics_accessed_without_active_handler" },
62+ .{ "20000" , "case_not_found" },
63+ .{ "21000" , "cardinality_violation" },
64+ .{ "22000" , "data_exception" },
65+ .{ "2202E" , "array_subscript_error" },
66+ .{ "22021" , "character_not_in_repertoire" },
67+ .{ "22008" , "datetime_field_overflow" },
68+ .{ "22012" , "division_by_zero" },
69+ .{ "22005" , "error_in_assignment" },
70+ .{ "2200B" , "escape_character_conflict" },
71+ .{ "22022" , "indicator_overflow" },
72+ .{ "22015" , "interval_field_overflow" },
73+ .{ "2201E" , "invalid_argument_for_logarithm" },
74+ .{ "22014" , "invalid_argument_for_ntile_function" },
75+ .{ "22016" , "invalid_argument_for_nth_value_function" },
76+ .{ "2201F" , "invalid_argument_for_power_function" },
77+ .{ "2201G" , "invalid_argument_for_width_bucket_function" },
78+ .{ "22018" , "invalid_character_value_for_cast" },
79+ .{ "22007" , "invalid_datetime_format" },
80+ .{ "22019" , "invalid_escape_character" },
81+ .{ "2200D" , "invalid_escape_octet" },
82+ .{ "22025" , "invalid_escape_sequence" },
83+ .{ "22P06" , "nonstandard_use_of_escape_character" },
84+ .{ "22010" , "invalid_indicator_parameter_value" },
85+ .{ "22023" , "invalid_parameter_value" },
86+ .{ "2201B" , "invalid_regular_expression" },
87+ .{ "2201W" , "invalid_row_count_in_limit_clause" },
88+ .{ "2201X" , "invalid_row_count_in_result_offset_clause" },
89+ .{ "2202H" , "invalid_tablesample_argument" },
90+ .{ "2202G" , "invalid_tablesample_repeat" },
91+ .{ "22009" , "invalid_time_zone_displacement_value" },
92+ .{ "2200C" , "invalid_use_of_escape_character" },
93+ .{ "2200G" , "most_specific_type_mismatch" },
94+ .{ "22004" , "null_value_not_allowed" },
95+ .{ "22002" , "null_value_no_indicator_parameter" },
96+ .{ "22003" , "numeric_value_out_of_range" },
97+ .{ "2200H" , "sequence_generator_limit_exceeded" },
98+ .{ "22026" , "string_data_length_mismatch" },
99+ .{ "22001" , "string_data_right_truncation" },
100+ .{ "22011" , "substring_error" },
101+ .{ "22027" , "trim_error" },
102+ .{ "22024" , "unterminated_c_string" },
103+ .{ "2200F" , "zero_length_character_string" },
104+ .{ "22P01" , "floating_point_exception" },
105+ .{ "22P02" , "invalid_text_representation" },
106+ .{ "22P03" , "invalid_binary_representation" },
107+ .{ "22P04" , "bad_copy_file_format" },
108+ .{ "22P05" , "untranslatable_character" },
109+ .{ "2200L" , "not_an_xml_document" },
110+ .{ "2200M" , "invalid_xml_document" },
111+ .{ "2200N" , "invalid_xml_content" },
112+ .{ "2200S" , "invalid_xml_comment" },
113+ .{ "2200T" , "invalid_xml_processing_instruction" },
114+ .{ "23000" , "integrity_constraint_violation" },
115+ .{ "23001" , "restrict_violation" },
116+ .{ "23502" , "not_null_violation" },
117+ .{ "23503" , "foreign_key_violation" },
118+ .{ "23505" , "unique_violation" },
119+ .{ "23514" , "check_violation" },
120+ .{ "23P01" , "exclusion_violation" },
121+ .{ "24000" , "invalid_cursor_state" },
122+ .{ "25000" , "invalid_transaction_state" },
123+ .{ "25001" , "active_sql_transaction" },
124+ .{ "25002" , "branch_transaction_already_active" },
125+ .{ "25008" , "held_cursor_requires_same_isolation_level" },
126+ .{ "25003" , "inappropriate_access_mode_for_branch_transaction" },
127+ .{ "25004" , "inappropriate_isolation_level_for_branch_transaction" },
128+ .{ "25005" , "no_active_sql_transaction_for_branch_transaction" },
129+ .{ "25006" , "read_only_sql_transaction" },
130+ .{ "25007" , "schema_and_data_statement_mixing_not_supported" },
131+ .{ "25P01" , "no_active_sql_transaction" },
132+ .{ "25P02" , "in_failed_sql_transaction" },
133+ .{ "25P03" , "idle_in_transaction_session_timeout" },
134+ .{ "26000" , "invalid_sql_statement_name" },
135+ .{ "27000" , "triggered_data_change_violation" },
136+ .{ "28000" , "invalid_authorization_specification" },
137+ .{ "28P01" , "invalid_password" },
138+ .{ "2B000" , "dependent_privilege_descriptors_still_exist" },
139+ .{ "2BP01" , "dependent_objects_still_exist" },
140+ .{ "2D000" , "invalid_transaction_termination" },
141+ .{ "2F000" , "sql_routine_exception" },
142+ .{ "2F005" , "function_executed_no_return_statement" },
143+ .{ "2F002" , "modifying_sql_data_not_permitted" },
144+ .{ "2F003" , "prohibited_sql_statement_attempted" },
145+ .{ "2F004" , "reading_sql_data_not_permitted" },
146+ .{ "34000" , "invalid_cursor_name" },
147+ .{ "38000" , "external_routine_exception" },
148+ .{ "38001" , "containing_sql_not_permitted" },
149+ .{ "38002" , "modifying_sql_data_not_permitted" },
150+ .{ "38003" , "prohibited_sql_statement_attempted" },
151+ .{ "38004" , "reading_sql_data_not_permitted" },
152+ .{ "39000" , "external_routine_invocation_exception" },
153+ .{ "39001" , "invalid_sqlstate_returned" },
154+ .{ "39004" , "null_value_not_allowed" },
155+ .{ "39P01" , "trigger_protocol_violated" },
156+ .{ "39P02" , "srf_protocol_violated" },
157+ .{ "39P03" , "event_trigger_protocol_violated" },
158+ .{ "3B000" , "savepoint_exception" },
159+ .{ "3B001" , "invalid_savepoint_specification" },
160+ .{ "3D000" , "invalid_catalog_name" },
161+ .{ "3F000" , "invalid_schema_name" },
162+ .{ "40000" , "transaction_rollback" },
163+ .{ "40002" , "transaction_integrity_constraint_violation" },
164+ .{ "40001" , "serialization_failure" },
165+ .{ "40003" , "statement_completion_unknown" },
166+ .{ "40P01" , "deadlock_detected" },
167+ .{ "42000" , "syntax_error_or_access_rule_violation" },
168+ .{ "42601" , "syntax_error" },
169+ .{ "42501" , "insufficient_privilege" },
170+ .{ "42846" , "cannot_coerce" },
171+ .{ "42803" , "grouping_error" },
172+ .{ "42P20" , "windowing_error" },
173+ .{ "42P19" , "invalid_recursion" },
174+ .{ "42830" , "invalid_foreign_key" },
175+ .{ "42602" , "invalid_name" },
176+ .{ "42622" , "name_too_long" },
177+ .{ "42939" , "reserved_name" },
178+ .{ "42804" , "datatype_mismatch" },
179+ .{ "42P18" , "indeterminate_datatype" },
180+ .{ "42P21" , "collation_mismatch" },
181+ .{ "42P22" , "indeterminate_collation" },
182+ .{ "42809" , "wrong_object_type" },
183+ .{ "428C9" , "generated_always" },
184+ .{ "42703" , "undefined_column" },
185+ .{ "42883" , "undefined_function" },
186+ .{ "42P01" , "undefined_table" },
187+ .{ "42P02" , "undefined_parameter" },
188+ .{ "42704" , "undefined_object" },
189+ .{ "42701" , "duplicate_column" },
190+ .{ "42P03" , "duplicate_cursor" },
191+ .{ "42P04" , "duplicate_database" },
192+ .{ "42723" , "duplicate_function" },
193+ .{ "42P05" , "duplicate_prepared_statement" },
194+ .{ "42P06" , "duplicate_schema" },
195+ .{ "42P07" , "duplicate_table" },
196+ .{ "42712" , "duplicate_alias" },
197+ .{ "42710" , "duplicate_object" },
198+ .{ "42702" , "ambiguous_column" },
199+ .{ "42725" , "ambiguous_function" },
200+ .{ "42P08" , "ambiguous_parameter" },
201+ .{ "42P09" , "ambiguous_alias" },
202+ .{ "42P10" , "invalid_column_reference" },
203+ .{ "42611" , "invalid_column_definition" },
204+ .{ "42P11" , "invalid_cursor_definition" },
205+ .{ "42P12" , "invalid_database_definition" },
206+ .{ "42P13" , "invalid_function_definition" },
207+ .{ "42P14" , "invalid_prepared_statement_definition" },
208+ .{ "42P15" , "invalid_schema_definition" },
209+ .{ "42P16" , "invalid_table_definition" },
210+ .{ "42P17" , "invalid_object_definition" },
211+ .{ "44000" , "with_check_option_violation" },
212+ .{ "53000" , "insufficient_resources" },
213+ .{ "53100" , "disk_full" },
214+ .{ "53200" , "out_of_memory" },
215+ .{ "53300" , "too_many_connections" },
216+ .{ "53400" , "configuration_limit_exceeded" },
217+ .{ "54000" , "program_limit_exceeded" },
218+ .{ "54001" , "statement_too_complex" },
219+ .{ "54011" , "too_many_columns" },
220+ .{ "54023" , "too_many_arguments" },
221+ .{ "55000" , "object_not_in_prerequisite_state" },
222+ .{ "55006" , "object_in_use" },
223+ .{ "55P02" , "cant_change_runtime_param" },
224+ .{ "55P03" , "lock_not_available" },
225+ .{ "55P04" , "unsafe_new_enum_value_usage" },
226+ .{ "57000" , "operator_intervention" },
227+ .{ "57014" , "query_canceled" },
228+ .{ "57P01" , "admin_shutdown" },
229+ .{ "57P02" , "crash_shutdown" },
230+ .{ "57P03" , "cannot_connect_now" },
231+ .{ "57P04" , "database_dropped" },
232+ .{ "58000" , "system_error" },
233+ .{ "58030" , "io_error" },
234+ .{ "58P01" , "undefined_file" },
235+ .{ "58P02" , "duplicate_file" },
236+ .{ "72000" , "snapshot_too_old" },
237+ .{ "F0000" , "config_file_error" },
238+ .{ "F0001" , "lock_file_exists" },
239+ .{ "HV000" , "fdw_error" },
240+ .{ "HV005" , "fdw_column_name_not_found" },
241+ .{ "HV002" , "fdw_dynamic_parameter_value_needed" },
242+ .{ "HV010" , "fdw_function_sequence_error" },
243+ .{ "HV021" , "fdw_inconsistent_descriptor_information" },
244+ .{ "HV024" , "fdw_invalid_attribute_value" },
245+ .{ "HV007" , "fdw_invalid_column_name" },
246+ .{ "HV008" , "fdw_invalid_column_number" },
247+ .{ "HV004" , "fdw_invalid_data_type" },
248+ .{ "HV006" , "fdw_invalid_data_type_descriptors" },
249+ .{ "HV091" , "fdw_invalid_descriptor_field_identifier" },
250+ .{ "HV00B" , "fdw_invalid_handle" },
251+ .{ "HV00C" , "fdw_invalid_option_index" },
252+ .{ "HV00D" , "fdw_invalid_option_name" },
253+ .{ "HV090" , "fdw_invalid_string_length_or_buffer_length" },
254+ .{ "HV00A" , "fdw_invalid_string_format" },
255+ .{ "HV009" , "fdw_invalid_use_of_null_pointer" },
256+ .{ "HV014" , "fdw_too_many_handles" },
257+ .{ "HV001" , "fdw_out_of_memory" },
258+ .{ "HV00P" , "fdw_no_schemas" },
259+ .{ "HV00J" , "fdw_option_name_not_found" },
260+ .{ "HV00K" , "fdw_reply_handle" },
261+ .{ "HV00Q" , "fdw_schema_not_found" },
262+ .{ "HV00R" , "fdw_table_not_found" },
263+ .{ "HV00L" , "fdw_unable_to_create_execution" },
264+ .{ "HV00M" , "fdw_unable_to_create_reply" },
265+ .{ "HV00N" , "fdw_unable_to_establish_connection" },
266+ .{ "P0000" , "plpgsql_error" },
267+ .{ "P0001" , "raise_exception" },
268+ .{ "P0002" , "no_data_found" },
269+ .{ "P0003" , "too_many_rows" },
270+ .{ "P0004" , "assert_failure" },
271+ .{ "XX000" , "internal_error" },
272+ .{ "XX001" , "data_corrupted" },
273+ .{ "XX002" , "index_corrupted" },
274+ });
275+
276+ fn getConditionName (error_code : String ) ? []const u8 {
277+ if (error_code .isEmpty ()) return null ;
278+
279+ const code_str = error_code .toUTF8WithoutRef (bun .default_allocator );
280+ defer code_str .deinit ();
281+
282+ return ERROR_CODE_MAP .get (code_str .slice ());
283+ }
284+
285+ const KeyValuePair = struct {
286+ key : []const u8 ,
287+ value : []const u8 ,
288+ };
289+
290+ fn parseKeyValueFromDetail (detail : String , allocator : std.mem.Allocator ) ? KeyValuePair {
291+ if (detail .isEmpty ()) return null ;
292+
293+ const detail_str = detail .toUTF8WithoutRef (allocator );
294+ defer detail_str .deinit ();
295+ const detail_slice = detail_str .slice ();
296+
297+ // Parse format: "Key (column_name)=(value) already exists."
298+ if (std .mem .indexOf (u8 , detail_slice , "Key (" )) | start | {
299+ const after_key = start + 5 ; // "Key (".len
300+ if (std .mem .indexOf (u8 , detail_slice [after_key .. ], ")=(" )) | end_key_relative | {
301+ const end_key = after_key + end_key_relative ;
302+ const key = detail_slice [after_key .. end_key ];
303+
304+ const value_start = end_key + 3 ; // ")=(".len
305+ if (std .mem .indexOf (u8 , detail_slice [value_start .. ], ") " )) | end_value_relative | {
306+ const end_value = value_start + end_value_relative ;
307+ const value = detail_slice [value_start .. end_value ];
308+
309+ // Allocate and copy the strings
310+ const key_copy = allocator .dupe (u8 , key ) catch return null ;
311+ const value_copy = allocator .dupe (u8 , value ) catch {
312+ allocator .free (key_copy );
313+ return null ;
314+ };
315+
316+ return KeyValuePair { .key = key_copy , .value = value_copy };
317+ }
318+ }
319+ }
320+
321+ return null ;
322+ }
323+
32324pub fn toJS (this : ErrorResponse , globalObject : * jsc.JSGlobalObject ) JSValue {
33325 var b = bun.StringBuilder {};
34326 defer b .deinit (bun .default_allocator );
@@ -106,6 +398,17 @@ pub fn toJS(this: ErrorResponse, globalObject: *jsc.JSGlobalObject) JSValue {
106398 }
107399 }
108400
401+ // Parse key/value from detail for unique constraint violations
402+ var key_value_data : ? KeyValuePair = null ;
403+ defer if (key_value_data ) | kv | {
404+ bun .default_allocator .free (kv .key );
405+ bun .default_allocator .free (kv .value );
406+ };
407+
408+ if (code .eqlComptime ("23505" ) and ! detail .isEmpty ()) {
409+ key_value_data = parseKeyValueFromDetail (detail , bun .default_allocator );
410+ }
411+
109412 const possible_fields = .{
110413 .{ "detail" , detail , void },
111414 .{ "hint" , hint , void },
@@ -143,6 +446,19 @@ pub fn toJS(this: ErrorResponse, globalObject: *jsc.JSGlobalObject) JSValue {
143446 }
144447 }
145448
449+ // Add condition name if we have an error code
450+ if (! code .isEmpty ()) {
451+ if (getConditionName (code )) | condition_name | {
452+ err .put (globalObject , jsc .ZigString .static ("condition" ), jsc .ZigString .init (condition_name ).toJS (globalObject ));
453+ }
454+ }
455+
456+ // Add key and value fields for unique constraint violations
457+ if (key_value_data ) | kv | {
458+ err .put (globalObject , jsc .ZigString .static ("key" ), jsc .ZigString .init (kv .key ).toJS (globalObject ));
459+ err .put (globalObject , jsc .ZigString .static ("value" ), jsc .ZigString .init (kv .value ).toJS (globalObject ));
460+ }
461+
146462 return err ;
147463}
148464
0 commit comments