Lightweight 0.20251202.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 SQLINTEGER nativeErrorCode {};
35 std::string sqlState = " "; // 5 characters + null terminator
36 std::string message;
37
38 /// Constructs an ODBC error info object from the given ODBC connection handle.
40 {
41 return FromHandle(SQL_HANDLE_DBC, hDbc);
42 }
43
44 /// Constructs an ODBC error info object from the given ODBC statement handle.
45 static SqlErrorInfo FromStatementHandle(SQLHSTMT hStmt)
46 {
47 return FromHandle(SQL_HANDLE_STMT, hStmt);
48 }
49
50 /// Constructs an ODBC error info object from the given ODBC environment handle.
52 {
53 return FromHandle(SQL_HANDLE_ENV, hEnv);
54 }
55
56 /// Asserts that the given result is a success code, otherwise throws an exception.
57 static void RequireStatementSuccess(SQLRETURN result, SQLHSTMT hStmt, std::string_view message);
58
59 private:
60 static SqlErrorInfo FromHandle(SQLSMALLINT handleType, SQLHANDLE handle)
61 {
62 SqlErrorInfo info {};
63 info.message = std::string(1024, '\0');
64
65 SQLSMALLINT msgLen {};
66 SQLGetDiagRecA(handleType,
67 handle,
68 1,
69 (SQLCHAR*) info.sqlState.data(),
70 &info.nativeErrorCode,
71 (SQLCHAR*) info.message.data(),
72 (SQLSMALLINT) info.message.capacity(),
73 &msgLen);
74 info.message.resize(static_cast<size_t>(msgLen));
75 return info;
76 }
77};
78
79class SqlException: public std::runtime_error
80{
81 public:
82 LIGHTWEIGHT_API explicit SqlException(SqlErrorInfo info,
83 std::source_location location = std::source_location::current());
84
85 // NOLINTNEXTLINE(readability-identifier-naming)
86 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE SqlErrorInfo const& info() const noexcept
87 {
88 return _info;
89 }
90
91 private:
92 SqlErrorInfo _info;
93};
94
95enum class SqlError : std::int16_t
96{
97 SUCCESS = SQL_SUCCESS,
98 SUCCESS_WITH_INFO = SQL_SUCCESS_WITH_INFO,
99 NODATA = SQL_NO_DATA,
100 FAILURE = SQL_ERROR,
101 INVALID_HANDLE = SQL_INVALID_HANDLE,
102 STILL_EXECUTING = SQL_STILL_EXECUTING,
103 NEED_DATA = SQL_NEED_DATA,
104 PARAM_DATA_AVAILABLE = SQL_PARAM_DATA_AVAILABLE,
105 NO_DATA_FOUND = SQL_NO_DATA_FOUND,
106 UNSUPPORTED_TYPE = 1'000,
107 INVALID_ARGUMENT = 1'001,
108 TRANSACTION_ERROR = 1'002,
109};
110
111struct SqlErrorCategory: std::error_category
112{
113 // NOLINTNEXTLINE(readability-identifier-naming)
114 static SqlErrorCategory const& get() noexcept
115 {
116 static SqlErrorCategory const category;
117 return category;
118 }
119
120 [[nodiscard]] char const* name() const noexcept override
121 {
122 return "Lightweight";
123 }
124
125 [[nodiscard]] std::string message(int code) const override
126 {
127 using namespace std::string_literals;
128 switch (static_cast<SqlError>(code))
129 {
130 case SqlError::SUCCESS:
131 return "SQL_SUCCESS"s;
132 case SqlError::SUCCESS_WITH_INFO:
133 return "SQL_SUCCESS_WITH_INFO"s;
134 case SqlError::NODATA:
135 return "SQL_NO_DATA"s;
136 case SqlError::FAILURE:
137 return "SQL_ERROR"s;
138 case SqlError::INVALID_HANDLE:
139 return "SQL_INVALID_HANDLE"s;
140 case SqlError::STILL_EXECUTING:
141 return "SQL_STILL_EXECUTING"s;
142 case SqlError::NEED_DATA:
143 return "SQL_NEED_DATA"s;
144 case SqlError::PARAM_DATA_AVAILABLE:
145 return "SQL_PARAM_DATA_AVAILABLE"s;
146 case SqlError::UNSUPPORTED_TYPE:
147 return "SQL_UNSUPPORTED_TYPE"s;
148 case SqlError::INVALID_ARGUMENT:
149 return "SQL_INVALID_ARGUMENT"s;
150 case SqlError::TRANSACTION_ERROR:
151 return "SQL_TRANSACTION_ERROR"s;
152 }
153 return std::format("SQL error code {}", code);
154 }
155};
156
157} // namespace Lightweight
158
159// Register our enum as an error code so we can constructor error_code from it
160template <>
161struct std::is_error_code_enum<Lightweight::SqlError>: public std::true_type
162{
163};
164
165/// Tells the compiler that MyErr pairs with MyCategory
166// NOLINTNEXTLINE(readability-identifier-naming)
167inline std::error_code make_error_code(Lightweight::SqlError e)
168{
169 return { static_cast<int>(e), Lightweight::SqlErrorCategory::get() };
170}
171
172template <>
173struct std::formatter<Lightweight::SqlError>: formatter<std::string>
174{
175 auto format(Lightweight::SqlError value, format_context& ctx) const -> format_context::iterator
176 {
177 return formatter<std::string>::format(
178 std::format("{}", Lightweight::SqlErrorCategory().message(static_cast<int>(value))), ctx);
179 }
180};
181
182template <>
183struct std::formatter<Lightweight::SqlErrorInfo>: formatter<std::string>
184{
185 auto format(Lightweight::SqlErrorInfo const& info, format_context& ctx) const -> format_context::iterator
186 {
187 return formatter<std::string>::format(std::format("{} ({}) - {}", info.sqlState, info.nativeErrorCode, info.message),
188 ctx);
189 }
190};
Represents an ODBC SQL error.
Definition SqlError.hpp:33
static void RequireStatementSuccess(SQLRETURN result, SQLHSTMT hStmt, std::string_view message)
Asserts that the given result is a success code, otherwise throws an exception.
static SqlErrorInfo FromStatementHandle(SQLHSTMT hStmt)
Constructs an ODBC error info object from the given ODBC statement handle.
Definition SqlError.hpp:45
static SqlErrorInfo FromEnvironmentHandle(SQLHENV hEnv)
Constructs an ODBC error info object from the given ODBC environment handle.
Definition SqlError.hpp:51
static SqlErrorInfo FromConnectionHandle(SQLHDBC hDbc)
Constructs an ODBC error info object from the given ODBC connection handle.
Definition SqlError.hpp:39