Lightweight 0.20260921.0
Loading...
Searching...
No Matches
SqlConnectInfo.hpp
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4
5#include "Api.hpp"
6
7#include <chrono>
8#include <cstddef>
9#include <cstdint>
10#include <format>
11#include <map>
12#include <string>
13#include <string_view>
14#include <variant>
15
16namespace Lightweight
17{
18
19/// @brief Default block-prefetch depth for new connections: the number of rows a classic per-row
20/// fetch loop requests per @c SQLFetchScroll round-trip on the transparent prefetch path.
21///
22/// Suffixed (not @c DefaultPrefetchDepth) so it does not collide with the
23/// @c SqlConnection::DefaultPrefetchDepth() accessor. A connection's depth can be overridden via
24/// @c SqlConnection::SetDefaultPrefetchDepth or @ref SqlConnectionDataSource::defaultPrefetchDepth;
25/// a value <= 1 disables prefetch.
26constexpr std::size_t PrefetchDepthDefault = 1000;
27
28/// @brief Default capacity of a connection's prepared-statement cache: the number of already-prepared
29/// ODBC statement handles kept alive for reuse.
30///
31/// Zero — the cache is opt-in. Reusing a prepared handle also reuses the query plan the driver derived
32/// from the schema at preparation time, so enabling it is a deliberate per-connection decision. See
33/// @c SqlConnection::SetPreparedStatementCacheCapacity and
34/// @ref SqlConnectionDataSource::preparedStatementCacheCapacity.
35inline constexpr std::size_t PreparedStatementCacheCapacityDefault = 0;
36
37/// @brief A sensible capacity for enabling the prepared-statement cache on a connection serving a
38/// bounded set of recurring queries (the typical DataMapper workload).
39inline constexpr std::size_t PreparedStatementCacheCapacitySuggested = 64;
40
41/// @ingroup CoreApi
42/// @brief Whether the client/server connection is TLS-encrypted.
43///
44/// Maps onto the Microsoft SQL Server ODBC connection attribute @c SQL_COPT_SS_ENCRYPT, which must be
45/// set on the connection handle *before* connecting. This is the only way to request encryption on the
46/// DSN-based connect path (@c SQLConnect), where there is no connection string for an @c Encrypt=
47/// keyword to live in.
48///
49/// @see https://learn.microsoft.com/en-us/sql/relational-databases/native-client-odbc-api/sqlsetconnectattr
50enum class SqlEncryptionMode : std::uint8_t
51{
52 /// Leave the attribute untouched — whatever the driver, DSN, or connection string configures wins.
53 ///
54 /// This is the default, so an application that does not opt in behaves exactly as before.
55 DriverDefault = 0,
56
57 /// Request an unencrypted connection (@c SQL_EN_OFF).
58 Disabled = 1,
59
60 /// Request an encrypted connection (@c SQL_EN_ON).
61 Enabled = 2,
62};
63
64/// Parses an ODBC @c Encrypt= connection-string value into a @ref SqlEncryptionMode.
65///
66/// Recognizes the spellings the SQL Server drivers accept, case-insensitively: @c yes / @c no,
67/// @c true / @c false, @c 1 / @c 0, and the ODBC Driver 18 synonyms @c mandatory / @c optional.
68///
69/// @warning @c SqlEncryptionMode has no representation for ODBC Driver 18's @c strict (TDS 8.0 with
70/// mandatory certificate validation), so @c Encrypt=strict parses as
71/// @c SqlEncryptionMode::DriverDefault and is *dropped* by a subsequent
72/// @ref SqlConnectionDataSource::ToConnectionString(). Keep such connection strings as a raw
73/// @ref SqlConnectionString instead of round-tripping them through a data source.
74///
75/// @param value The raw keyword value.
76/// @return The matching mode, or @c SqlEncryptionMode::DriverDefault if @p value is not recognized.
77[[nodiscard]] LIGHTWEIGHT_API SqlEncryptionMode ParseEncryptionMode(std::string_view value) noexcept;
78
79/// Renders a @ref SqlEncryptionMode as the ODBC @c Encrypt= connection-string value.
80///
81/// @param mode The mode to render.
82/// @return @c "yes" or @c "no", or an empty view for @c SqlEncryptionMode::DriverDefault (which is
83/// expressed by omitting the keyword entirely).
84[[nodiscard]] LIGHTWEIGHT_API std::string_view FormatEncryptionMode(SqlEncryptionMode mode) noexcept;
85
86/// @ingroup CoreApi
87/// Represents an ODBC connection string.
89{
90 /// The raw ODBC connection string value.
91 std::string value;
92
93 /// Three-way comparison operator.
94 auto operator<=>(SqlConnectionString const&) const noexcept = default;
95
96 /// Returns a sanitized copy of the connection string with the password masked.
97 [[nodiscard]] LIGHTWEIGHT_API std::string Sanitized() const;
98
99 /// Sanitizes the password in the given connection string input.
100 [[nodiscard]] LIGHTWEIGHT_API static std::string SanitizePwd(std::string_view input);
101};
102
103using SqlConnectionStringMap = std::map<std::string, std::string>;
104
105/// Parses an ODBC connection string into a map.
106LIGHTWEIGHT_API SqlConnectionStringMap ParseConnectionString(SqlConnectionString const& connectionString);
107
108/// Builds an ODBC connection string from a map.
109LIGHTWEIGHT_API SqlConnectionString BuildConnectionString(SqlConnectionStringMap const& map);
110
111/// If `connectionString` targets a file-based SQLite database, ensures the
112/// parent directory exists and touches an empty file when missing.
113///
114/// An empty file is a valid zero-table SQLite database, so this lets callers
115/// bootstrap a fresh SQLite deployment from scratch without requiring the
116/// user to pre-create the file. In-memory databases (`:memory:`,
117/// `file::memory:`, URIs with `mode=memory`) and non-SQLite drivers are
118/// left untouched.
119///
120/// Returns true on success or when no action was needed. Returns false only
121/// when the parent directory could not be created or the file could not be
122/// opened for writing.
123[[nodiscard]] LIGHTWEIGHT_API bool EnsureSqliteDatabaseFileExists(SqlConnectionString const& connectionString);
124
125/// @ingroup CoreApi
126/// Represents a connection data source as a DSN, username, password, and timeout.
127struct [[nodiscard]] SqlConnectionDataSource
128{
129 /// The ODBC data source name (DSN).
130 std::string datasource;
131 /// The username for authentication.
132 std::string username;
133 /// The password for authentication.
134 std::string password;
135 /// The connection timeout duration.
136 std::chrono::seconds timeout { 5 };
137 /// @brief Default block-prefetch depth applied to statements created on the resulting connection
138 /// (rows requested per @c SQLFetchScroll round-trip on the transparent per-row fetch path).
139 ///
140 /// A value <= 1 disables prefetch (every classic loop keeps issuing one @c SQLFetch per row).
141 /// Defaults to @c PrefetchDepthDefault. Has effect only on backends whose driver supports
142 /// native row-array fetching (see @c SqlConnection::SupportsNativeRowArrayFetch).
143 std::size_t defaultPrefetchDepth = PrefetchDepthDefault;
144
145 /// @brief Whether to request a TLS-encrypted connection.
146 ///
147 /// Defaults to @c SqlEncryptionMode::DriverDefault, which leaves the driver's own configuration in
148 /// charge. Any other value is applied to the connection handle before connecting, and a driver that
149 /// rejects it fails the connection rather than silently downgrading to plaintext.
150 SqlEncryptionMode encryption = SqlEncryptionMode::DriverDefault;
151
152 /// @brief Capacity of the prepared-statement cache on the resulting connection: how many
153 /// already-prepared ODBC statement handles are kept alive so that re-preparing the same SQL text
154 /// re-executes one instead of paying the server-side parse again.
155 ///
156 /// Defaults to @c PreparedStatementCacheCapacityDefault (zero, i.e. disabled). See
157 /// @c SqlConnection::SetPreparedStatementCacheCapacity for the implications of enabling it.
158 std::size_t preparedStatementCacheCapacity = PreparedStatementCacheCapacityDefault;
159
160 /// Constructs a SqlConnectionDataSource from the given connection string.
162
163 /// Converts this data source to an ODBC connection string.
164 ///
165 /// The @c Encrypt= keyword is emitted only when @ref encryption is not
166 /// @c SqlEncryptionMode::DriverDefault, so the rendering of a data source that did not opt in is
167 /// byte-for-byte what it always was.
168 [[nodiscard]] LIGHTWEIGHT_API SqlConnectionString ToConnectionString() const
169 {
170 auto value = std::format("DSN={};UID={};PWD={};TIMEOUT={}", datasource, username, password, timeout.count());
171 if (auto const encryptValue = FormatEncryptionMode(encryption); !encryptValue.empty())
172 value += std::format(";Encrypt={}", encryptValue);
173 return SqlConnectionString { .value = std::move(value) };
174 }
175
176 /// Three-way comparison operator.
177 auto operator<=>(SqlConnectionDataSource const&) const noexcept = default;
178};
179
180using SqlConnectInfo = std::variant<SqlConnectionDataSource, SqlConnectionString>;
181
182} // namespace Lightweight
183
184template <>
185struct std::formatter<Lightweight::SqlConnectInfo>: std::formatter<std::string>
186{
187 auto format(Lightweight::SqlConnectInfo const& info, format_context& ctx) const -> format_context::iterator
188 {
189 if (auto const* dsn = std::get_if<Lightweight::SqlConnectionDataSource>(&info))
190 {
191 return formatter<string>::format(dsn->ToConnectionString().value, ctx);
192 }
193 else if (auto const* connectionString = std::get_if<Lightweight::SqlConnectionString>(&info))
194 {
195 return formatter<string>::format(connectionString->value, ctx);
196 }
197 else
198 {
199 return formatter<string>::format("Invalid connection info", ctx);
200 }
201 }
202};
SqlEncryptionMode
Whether the client/server connection is TLS-encrypted.
@ Enabled
Request an encrypted connection (SQL_EN_ON).
@ Disabled
Request an unencrypted connection (SQL_EN_OFF).
auto operator<=>(SqlConnectionDataSource const &) const noexcept=default
Three-way comparison operator.
static LIGHTWEIGHT_API SqlConnectionDataSource FromConnectionString(SqlConnectionString const &value)
Constructs a SqlConnectionDataSource from the given connection string.
LIGHTWEIGHT_API SqlConnectionString ToConnectionString() const
std::string datasource
The ODBC data source name (DSN).
std::string password
The password for authentication.
std::string username
The username for authentication.
auto operator<=>(SqlConnectionString const &) const noexcept=default
Three-way comparison operator.
static LIGHTWEIGHT_API std::string SanitizePwd(std::string_view input)
Sanitizes the password in the given connection string input.
std::string value
The raw ODBC connection string value.
LIGHTWEIGHT_API std::string Sanitized() const
Returns a sanitized copy of the connection string with the password masked.