Lightweight 0.20251202.0
Loading...
Searching...
No Matches
SqlErrorDetection.hpp
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4
5#include "SqlError.hpp"
6#include "SqlServerType.hpp"
7
8#include <string_view>
9
10namespace Lightweight
11{
12
13/// Detects if an error indicates "table already exists".
14///
15/// @param error The SQL error info
16/// @param serverType The database server type
17/// @return true if this is a "table already exists" error
18[[nodiscard]] inline bool IsTableAlreadyExistsError(SqlErrorInfo const& error, SqlServerType serverType) noexcept
19{
20 switch (serverType)
21 {
22 case SqlServerType::MICROSOFT_SQL:
23 // SQLSTATE 42S01 = Base table or view already exists
24 // Native error 2714 = "There is already an object named '...' in the database"
25 return error.sqlState == "42S01" || error.nativeErrorCode == 2714;
26 case SqlServerType::POSTGRESQL:
27 // SQLSTATE 42P07 = duplicate_table
28 return error.sqlState == "42P07";
29 case SqlServerType::SQLITE:
30 // SQLite uses generic SQLSTATE, check message
31 return error.message.find("already exists") != std::string::npos;
32 case SqlServerType::MYSQL:
33 // SQLSTATE 42S01 = Base table or view already exists
34 // Native error 1050 = "Table '...' already exists"
35 return error.sqlState == "42S01" || error.nativeErrorCode == 1050;
36 default:
37 // Fallback: check message for common pattern
38 return error.message.find("already exists") != std::string::npos;
39 }
40}
41
42/// Detects if an error indicates "table not found".
43///
44/// @param error The SQL error info
45/// @param serverType The database server type
46/// @return true if this is a "table not found" error
47[[nodiscard]] inline bool IsTableNotFoundError(SqlErrorInfo const& error, SqlServerType serverType) noexcept
48{
49 switch (serverType)
50 {
51 case SqlServerType::MICROSOFT_SQL:
52 // SQLSTATE 42S02 = Base table or view not found
53 // Native error 208 = "Invalid object name '...'"
54 return error.sqlState == "42S02" || error.nativeErrorCode == 208;
55 case SqlServerType::POSTGRESQL:
56 // SQLSTATE 42P01 = undefined_table
57 return error.sqlState == "42P01";
58 case SqlServerType::SQLITE:
59 // SQLite uses generic SQLSTATE, check message
60 return error.message.find("no such table") != std::string::npos;
61 case SqlServerType::MYSQL:
62 // SQLSTATE 42S02 = Base table or view not found
63 // Native error 1146 = "Table '...' doesn't exist"
64 return error.sqlState == "42S02" || error.nativeErrorCode == 1146;
65 default:
66 // Fallback: check message for common patterns
67 return error.message.find("not found") != std::string::npos || error.message.find("no such") != std::string::npos
68 || error.message.find("does not exist") != std::string::npos;
69 }
70}
71
72/// Detects if an error is transient (retryable).
73///
74/// Transient errors are temporary conditions that may succeed if retried,
75/// such as connection issues, timeouts, or database locks.
76///
77/// @param error The SQL error info
78/// @return true if this is a transient error that may be retried
79[[nodiscard]] inline bool IsTransientError(SqlErrorInfo const& error) noexcept
80{
81 std::string_view state = error.sqlState;
82
83 // Connection errors (SQLSTATE Class 08)
84 // 08001 = Unable to connect
85 // 08003 = Connection does not exist
86 // 08004 = Connection rejected
87 // 08006 = Connection failure
88 // 08007 = Transaction resolution unknown
89 if (state.starts_with("08"))
90 return true;
91
92 // Timeout errors
93 // HYT00 = Timeout expired
94 // HYT01 = Connection timeout expired
95 if (state == "HYT00" || state == "HYT01")
96 return true;
97
98 // Transaction rollback (SQLSTATE Class 40)
99 // 40001 = Serialization failure (deadlock)
100 // 40002 = Transaction integrity constraint violation
101 // 40003 = Statement completion unknown
102 if (state.starts_with("40"))
103 return true;
104
105 // SQLite-specific busy/locked conditions
106 if (error.message.find("database is locked") != std::string::npos)
107 return true;
108 if (error.message.find("SQLITE_BUSY") != std::string::npos)
109 return true;
110 if (error.message.find("SQLITE_LOCKED") != std::string::npos)
111 return true;
112
113 // SQL Server specific transient errors
114 // Native error codes for transient conditions
115 switch (error.nativeErrorCode)
116 {
117 case 1205: // Deadlock victim (SQL Server)
118 case 1222: // Lock request timeout (SQL Server)
119 case -2: // Timeout (SQL Server)
120 return true;
121 default:
122 break;
123 }
124
125 return false;
126}
127
128/// Detects if an error indicates a unique constraint violation.
129///
130/// @param error The SQL error info
131/// @param serverType The database server type
132/// @return true if this is a unique constraint violation error
133[[nodiscard]] inline bool IsUniqueConstraintViolation(SqlErrorInfo const& error, SqlServerType serverType) noexcept
134{
135 switch (serverType)
136 {
137 case SqlServerType::MICROSOFT_SQL:
138 // SQLSTATE 23000 = Integrity constraint violation
139 // Native error 2627 = Violation of UNIQUE KEY constraint
140 // Native error 2601 = Cannot insert duplicate key row
141 return error.nativeErrorCode == 2627 || error.nativeErrorCode == 2601;
142 case SqlServerType::POSTGRESQL:
143 // SQLSTATE 23505 = unique_violation
144 return error.sqlState == "23505";
145 case SqlServerType::SQLITE:
146 // SQLite uses generic SQLSTATE, check message
147 return error.message.find("UNIQUE constraint failed") != std::string::npos;
148 case SqlServerType::MYSQL:
149 // Native error 1062 = Duplicate entry for key
150 return error.nativeErrorCode == 1062;
151 default:
152 return error.sqlState == "23000" || error.sqlState == "23505";
153 }
154}
155
156/// Detects if an error indicates a foreign key constraint violation.
157///
158/// @param error The SQL error info
159/// @param serverType The database server type
160/// @return true if this is a foreign key constraint violation error
161[[nodiscard]] inline bool IsForeignKeyViolation(SqlErrorInfo const& error, SqlServerType serverType) noexcept
162{
163 switch (serverType)
164 {
165 case SqlServerType::MICROSOFT_SQL:
166 // Native error 547 = The INSERT/UPDATE/DELETE statement conflicted with FK constraint
167 return error.nativeErrorCode == 547;
168 case SqlServerType::POSTGRESQL:
169 // SQLSTATE 23503 = foreign_key_violation
170 return error.sqlState == "23503";
171 case SqlServerType::SQLITE:
172 // SQLite uses generic SQLSTATE, check message
173 return error.message.find("FOREIGN KEY constraint failed") != std::string::npos;
174 case SqlServerType::MYSQL:
175 // Native error 1451 = Cannot delete or update a parent row
176 // Native error 1452 = Cannot add or update a child row
177 return error.nativeErrorCode == 1451 || error.nativeErrorCode == 1452;
178 default:
179 return error.sqlState == "23503";
180 }
181}
182
183} // namespace Lightweight