Lightweight 0.20260303.0
Loading...
Searching...
No Matches
SqlError.hpp
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4
5#if defined(_WIN32) || defined(_WIN64)
6 #include <Windows.h>
7#endif
8
9#include "Api.hpp"
10
11#include <cstdint>
12#include <format>
13#include <source_location>
14#include <stdexcept>
15#include <system_error>
16
17#include <sql.h>
18#include <sqlext.h>
19#include <sqlspi.h>
20#include <sqltypes.h>
21
22namespace Lightweight
23{
24
25/// @brief Represents an ODBC SQL error.
26///
27/// NOTE: This is a simple wrapper around the SQL return codes. It is not meant to be
28/// comprehensive, but rather to provide a simple way to convert SQL return codes to
29/// std::error_code.
30///
31/// The code below is DRAFT and may be subject to change.
33{
34 /// The native ODBC error code.
35 SQLINTEGER nativeErrorCode {};
36 /// The SQLSTATE diagnostic code (5 characters).
37 std::string sqlState = " "; // 5 characters + null terminator
38 /// The human-readable error message.
39 std::string message;
40
41 /// Constructs an ODBC error info object from the given ODBC connection handle.
43 {
44 return FromHandle(SQL_HANDLE_DBC, hDbc);
45 }
46
47 /// Constructs an ODBC error info object from the given ODBC statement handle.
48 static SqlErrorInfo FromStatementHandle(SQLHSTMT hStmt)
49 {
50 return FromHandle(SQL_HANDLE_STMT, hStmt);
51 }
52
53 /// Constructs an ODBC error info object from the given ODBC environment handle.
55 {
56 return FromHandle(SQL_HANDLE_ENV, hEnv);
57 }
58
59 /// Asserts that the given result is a success code, otherwise throws an exception.
60 static void RequireStatementSuccess(SQLRETURN result, SQLHSTMT hStmt, std::string_view message);
61
62 private:
63 static SqlErrorInfo FromHandle(SQLSMALLINT handleType, SQLHANDLE handle)
64 {
65 SqlErrorInfo info {};
66 info.message = std::string(1024, '\0');
67
68 SQLSMALLINT msgLen {};
69 SQLGetDiagRecA(handleType,
70 handle,
71 1,
72 (SQLCHAR*) info.sqlState.data(),
73 &info.nativeErrorCode,
74 (SQLCHAR*) info.message.data(),
75 (SQLSMALLINT) info.message.capacity(),
76 &msgLen);
77 info.message.resize(static_cast<size_t>(msgLen));
78 return info;
79 }
80};
81
82class SqlException: public std::runtime_error
83{
84 public:
85 LIGHTWEIGHT_API explicit SqlException(SqlErrorInfo info,
86 std::source_location location = std::source_location::current());
87
88 // NOLINTNEXTLINE(readability-identifier-naming)
89 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE SqlErrorInfo const& info() const noexcept
90 {
91 return _info;
92 }
93
94 private:
95 SqlErrorInfo _info;
96};
97
98enum class SqlError : std::int16_t
99{
100 SUCCESS = SQL_SUCCESS,
101 SUCCESS_WITH_INFO = SQL_SUCCESS_WITH_INFO,
102 NODATA = SQL_NO_DATA,
103 FAILURE = SQL_ERROR,
104 INVALID_HANDLE = SQL_INVALID_HANDLE,
105 STILL_EXECUTING = SQL_STILL_EXECUTING,
106 NEED_DATA = SQL_NEED_DATA,
107 PARAM_DATA_AVAILABLE = SQL_PARAM_DATA_AVAILABLE,
108 NO_DATA_FOUND = SQL_NO_DATA_FOUND,
109 UNSUPPORTED_TYPE = 1'000,
110 INVALID_ARGUMENT = 1'001,
111 TRANSACTION_ERROR = 1'002,
112};
113
114struct SqlErrorCategory: std::error_category
115{
116 // NOLINTNEXTLINE(readability-identifier-naming)
117 static SqlErrorCategory const& get() noexcept
118 {
119 static SqlErrorCategory const category;
120 return category;
121 }
122
123 [[nodiscard]] char const* name() const noexcept override
124 {
125 return "Lightweight";
126 }
127
128 [[nodiscard]] std::string message(int code) const override
129 {
130 using namespace std::string_literals;
131 switch (static_cast<SqlError>(code))
132 {
133 case SqlError::SUCCESS:
134 return "SQL_SUCCESS"s;
135 case SqlError::SUCCESS_WITH_INFO:
136 return "SQL_SUCCESS_WITH_INFO"s;
137 case SqlError::NODATA:
138 return "SQL_NO_DATA"s;
139 case SqlError::FAILURE:
140 return "SQL_ERROR"s;
141 case SqlError::INVALID_HANDLE:
142 return "SQL_INVALID_HANDLE"s;
143 case SqlError::STILL_EXECUTING:
144 return "SQL_STILL_EXECUTING"s;
145 case SqlError::NEED_DATA:
146 return "SQL_NEED_DATA"s;
147 case SqlError::PARAM_DATA_AVAILABLE:
148 return "SQL_PARAM_DATA_AVAILABLE"s;
149 case SqlError::UNSUPPORTED_TYPE:
150 return "SQL_UNSUPPORTED_TYPE"s;
151 case SqlError::INVALID_ARGUMENT:
152 return "SQL_INVALID_ARGUMENT"s;
153 case SqlError::TRANSACTION_ERROR:
154 return "SQL_TRANSACTION_ERROR"s;
155 }
156 return std::format("SQL error code {}", code);
157 }
158};
159
160} // namespace Lightweight
161
162// Register our enum as an error code so we can constructor error_code from it
163template <>
164struct std::is_error_code_enum<Lightweight::SqlError>: public std::true_type
165{
166};
167
168/// Tells the compiler that MyErr pairs with MyCategory
169// NOLINTNEXTLINE(readability-identifier-naming)
170inline std::error_code make_error_code(Lightweight::SqlError e)
171{
172 return { static_cast<int>(e), Lightweight::SqlErrorCategory::get() };
173}
174
175template <>
176struct std::formatter<Lightweight::SqlError>: formatter<std::string>
177{
178 auto format(Lightweight::SqlError value, format_context& ctx) const -> format_context::iterator
179 {
180 return formatter<std::string>::format(
181 std::format("{}", Lightweight::SqlErrorCategory().message(static_cast<int>(value))), ctx);
182 }
183};
184
185template <>
186struct std::formatter<Lightweight::SqlErrorInfo>: formatter<std::string>
187{
188 auto format(Lightweight::SqlErrorInfo const& info, format_context& ctx) const -> format_context::iterator
189 {
190 return formatter<std::string>::format(std::format("{} ({}) - {}", info.sqlState, info.nativeErrorCode, info.message),
191 ctx);
192 }
193};
Represents an ODBC SQL error.
Definition SqlError.hpp:33
SQLINTEGER nativeErrorCode
The native ODBC error code.
Definition SqlError.hpp:35
std::string message
The human-readable error message.
Definition SqlError.hpp:39
static void RequireStatementSuccess(SQLRETURN result, SQLHSTMT hStmt, std::string_view message)
Asserts that the given result is a success code, otherwise throws an exception.
std::string sqlState
The SQLSTATE diagnostic code (5 characters).
Definition SqlError.hpp:37
static SqlErrorInfo FromStatementHandle(SQLHSTMT hStmt)
Constructs an ODBC error info object from the given ODBC statement handle.
Definition SqlError.hpp:48
static SqlErrorInfo FromEnvironmentHandle(SQLHENV hEnv)
Constructs an ODBC error info object from the given ODBC environment handle.
Definition SqlError.hpp:54
static SqlErrorInfo FromConnectionHandle(SQLHDBC hDbc)
Constructs an ODBC error info object from the given ODBC connection handle.
Definition SqlError.hpp:42