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 LIGHTWEIGHT_API static SqlErrorInfo FromHandle(SQLSMALLINT handleType, SQLHANDLE handle);
64};
65
66class SqlException: public std::runtime_error
67{
68 public:
69 LIGHTWEIGHT_API explicit SqlException(SqlErrorInfo info,
70 std::source_location location = std::source_location::current());
71
72 // NOLINTNEXTLINE(readability-identifier-naming)
73 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE SqlErrorInfo const& info() const noexcept
74 {
75 return _info;
76 }
77
78 private:
79 SqlErrorInfo _info;
80};
81
82enum class SqlError : std::int16_t
83{
84 SUCCESS = SQL_SUCCESS,
85 SUCCESS_WITH_INFO = SQL_SUCCESS_WITH_INFO,
86 NODATA = SQL_NO_DATA,
87 FAILURE = SQL_ERROR,
88 INVALID_HANDLE = SQL_INVALID_HANDLE,
89 STILL_EXECUTING = SQL_STILL_EXECUTING,
90 NEED_DATA = SQL_NEED_DATA,
91 PARAM_DATA_AVAILABLE = SQL_PARAM_DATA_AVAILABLE,
92 NO_DATA_FOUND = SQL_NO_DATA_FOUND,
93 UNSUPPORTED_TYPE = 1'000,
94 INVALID_ARGUMENT = 1'001,
95 TRANSACTION_ERROR = 1'002,
96};
97
98struct SqlErrorCategory: std::error_category
99{
100 // NOLINTNEXTLINE(readability-identifier-naming)
101 static SqlErrorCategory const& get() noexcept
102 {
103 static SqlErrorCategory const category;
104 return category;
105 }
106
107 [[nodiscard]] char const* name() const noexcept override
108 {
109 return "Lightweight";
110 }
111
112 [[nodiscard]] std::string message(int code) const override
113 {
114 using namespace std::string_literals;
115 switch (static_cast<SqlError>(code))
116 {
117 case SqlError::SUCCESS:
118 return "SQL_SUCCESS"s;
119 case SqlError::SUCCESS_WITH_INFO:
120 return "SQL_SUCCESS_WITH_INFO"s;
121 case SqlError::NODATA:
122 return "SQL_NO_DATA"s;
123 case SqlError::FAILURE:
124 return "SQL_ERROR"s;
125 case SqlError::INVALID_HANDLE:
126 return "SQL_INVALID_HANDLE"s;
127 case SqlError::STILL_EXECUTING:
128 return "SQL_STILL_EXECUTING"s;
129 case SqlError::NEED_DATA:
130 return "SQL_NEED_DATA"s;
131 case SqlError::PARAM_DATA_AVAILABLE:
132 return "SQL_PARAM_DATA_AVAILABLE"s;
133 case SqlError::UNSUPPORTED_TYPE:
134 return "SQL_UNSUPPORTED_TYPE"s;
135 case SqlError::INVALID_ARGUMENT:
136 return "SQL_INVALID_ARGUMENT"s;
137 case SqlError::TRANSACTION_ERROR:
138 return "SQL_TRANSACTION_ERROR"s;
139 }
140 return std::format("SQL error code {}", code);
141 }
142};
143
144} // namespace Lightweight
145
146// Register our enum as an error code so we can constructor error_code from it
147template <>
148struct std::is_error_code_enum<Lightweight::SqlError>: public std::true_type
149{
150};
151
152/// Tells the compiler that MyErr pairs with MyCategory
153// NOLINTNEXTLINE(readability-identifier-naming)
154inline std::error_code make_error_code(Lightweight::SqlError e)
155{
156 return { static_cast<int>(e), Lightweight::SqlErrorCategory::get() };
157}
158
159template <>
160struct std::formatter<Lightweight::SqlError>: formatter<std::string>
161{
162 auto format(Lightweight::SqlError value, format_context& ctx) const -> format_context::iterator
163 {
164 return formatter<std::string>::format(
165 std::format("{}", Lightweight::SqlErrorCategory().message(static_cast<int>(value))), ctx);
166 }
167};
168
169template <>
170struct std::formatter<Lightweight::SqlErrorInfo>: formatter<std::string>
171{
172 auto format(Lightweight::SqlErrorInfo const& info, format_context& ctx) const -> format_context::iterator
173 {
174 return formatter<std::string>::format(std::format("{} ({}) - {}", info.sqlState, info.nativeErrorCode, info.message),
175 ctx);
176 }
177};
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