Lightweight 0.20261105.1
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 copy of the connection string with every password value masked, see @ref SanitizePwd.
97 [[nodiscard]] LIGHTWEIGHT_API std::string Sanitized() const;
98
99 /// Returns a copy of @p input with the values of its @c PWD= and @c Password= attributes replaced
100 /// by @c ***, for logging and diagnostics.
101 ///
102 /// The string is parsed attribute-wise following the ODBC quoting rules (see
103 /// @c ParseConnectionString()), so a brace-quoted password such as @c PWD={p;w} is masked as a
104 /// whole and a @c ; inside another quoted value does not start a new attribute. Attribute names
105 /// match case-insensitively and only as a whole (@c MyPWD= and @c Database=PasswordVault are left
106 /// alone); everything that is not a password value, including the spelling of the keys and any
107 /// whitespace, is copied verbatim.
108 ///
109 /// @param input The raw connection string.
110 /// @return The masked copy.
111 [[nodiscard]] LIGHTWEIGHT_API static std::string SanitizePwd(std::string_view input);
112};
113
114using SqlConnectionStringMap = std::map<std::string, std::string>;
115
116/// Parses an ODBC connection string into a map.
117///
118/// Keys are upper-cased. Values follow the driver managers' quoting rules: a value that starts
119/// with @c { runs to the matching @c } even across @c ; characters, and a doubled @c }} inside it
120/// stands for one literal @c }. A value without a closing brace runs to the end of the string.
121LIGHTWEIGHT_API SqlConnectionStringMap ParseConnectionString(SqlConnectionString const& connectionString);
122
123/// Builds an ODBC connection string from a map.
124///
125/// Every value is rendered through @c FormatConnectionStringValue(), so the result parses back
126/// into the same map whatever characters the values contain.
127LIGHTWEIGHT_API SqlConnectionString BuildConnectionString(SqlConnectionStringMap const& map);
128
129/// Renders @p value so that it can be spliced after @c KEY= in an ODBC connection string.
130///
131/// A value containing one of the connection-string metacharacters @c ; @c = @c { @c }, or starting
132/// or ending with whitespace (which every parser trims away), is wrapped in braces with every
133/// embedded @c } doubled — @c p;w becomes @c {p;w} and @c a}b becomes @c {a}}b}. That is the quoting
134/// ODBC defines for attribute values: the driver managers and the SQL Server and PostgreSQL drivers
135/// read it back, whereas the SQLite ODBC driver takes a braced @c Database= path literally. Any
136/// other value is returned verbatim, so the common case keeps its plain spelling.
137///
138/// @param value The raw attribute value.
139/// @return The spelling to splice after @c KEY=.
140[[nodiscard]] LIGHTWEIGHT_API std::string FormatConnectionStringValue(std::string_view value);
141
142/// If `connectionString` targets a file-based SQLite database, ensures the
143/// parent directory exists and touches an empty file when missing.
144///
145/// An empty file is a valid zero-table SQLite database, so this lets callers
146/// bootstrap a fresh SQLite deployment from scratch without requiring the
147/// user to pre-create the file. In-memory databases (`:memory:`,
148/// `file::memory:`, URIs with `mode=memory`) and non-SQLite drivers are
149/// left untouched.
150///
151/// Returns true on success or when no action was needed. Returns false only
152/// when the parent directory could not be created or the file could not be
153/// opened for writing.
154[[nodiscard]] LIGHTWEIGHT_API bool EnsureSqliteDatabaseFileExists(SqlConnectionString const& connectionString);
155
156/// @ingroup CoreApi
157/// Represents a connection data source as a DSN, username, password, and timeout.
158struct [[nodiscard]] SqlConnectionDataSource
159{
160 /// The ODBC data source name (DSN).
161 std::string datasource;
162 /// The username for authentication.
163 std::string username;
164 /// The password for authentication.
165 std::string password;
166 /// The connection timeout duration.
167 std::chrono::seconds timeout { 5 };
168 /// @brief Default block-prefetch depth applied to statements created on the resulting connection
169 /// (rows requested per @c SQLFetchScroll round-trip on the transparent per-row fetch path).
170 ///
171 /// A value <= 1 disables prefetch (every classic loop keeps issuing one @c SQLFetch per row).
172 /// Defaults to @c PrefetchDepthDefault. Has effect only on backends whose driver supports
173 /// native row-array fetching (see @c SqlConnection::SupportsNativeRowArrayFetch).
174 std::size_t defaultPrefetchDepth = PrefetchDepthDefault;
175
176 /// @brief Whether to request a TLS-encrypted connection.
177 ///
178 /// Defaults to @c SqlEncryptionMode::DriverDefault, which leaves the driver's own configuration in
179 /// charge. Any other value is applied to the connection handle before connecting, and a driver that
180 /// rejects it fails the connection rather than silently downgrading to plaintext.
181 SqlEncryptionMode encryption = SqlEncryptionMode::DriverDefault;
182
183 /// @brief Capacity of the prepared-statement cache on the resulting connection: how many
184 /// already-prepared ODBC statement handles are kept alive so that re-preparing the same SQL text
185 /// re-executes one instead of paying the server-side parse again.
186 ///
187 /// Defaults to @c PreparedStatementCacheCapacityDefault (zero, i.e. disabled). See
188 /// @c SqlConnection::SetPreparedStatementCacheCapacity for the implications of enabling it.
189 std::size_t preparedStatementCacheCapacity = PreparedStatementCacheCapacityDefault;
190
191 /// Constructs a SqlConnectionDataSource from the given connection string.
193
194 /// Converts this data source to an ODBC connection string.
195 ///
196 /// The @c Encrypt= keyword is emitted only when @ref encryption is not
197 /// @c SqlEncryptionMode::DriverDefault, so the rendering of a data source that did not opt in is
198 /// byte-for-byte what it always was.
199 ///
200 /// @ref datasource, @ref username and @ref password go through @c FormatConnectionStringValue(),
201 /// so a password such as @c p;w reaches the driver intact while plain credentials keep their
202 /// unquoted spelling.
203 [[nodiscard]] LIGHTWEIGHT_API SqlConnectionString ToConnectionString() const;
204
205 /// Three-way comparison operator.
206 auto operator<=>(SqlConnectionDataSource const&) const noexcept = default;
207};
208
209using SqlConnectInfo = std::variant<SqlConnectionDataSource, SqlConnectionString>;
210
211} // namespace Lightweight
212
213template <>
214struct std::formatter<Lightweight::SqlConnectInfo>: std::formatter<std::string>
215{
216 auto format(Lightweight::SqlConnectInfo const& info, format_context& ctx) const -> format_context::iterator
217 {
218 if (auto const* dsn = std::get_if<Lightweight::SqlConnectionDataSource>(&info))
219 {
220 return formatter<string>::format(dsn->ToConnectionString().value, ctx);
221 }
222 else if (auto const* connectionString = std::get_if<Lightweight::SqlConnectionString>(&info))
223 {
224 return formatter<string>::format(connectionString->value, ctx);
225 }
226 else
227 {
228 return formatter<string>::format("Invalid connection info", ctx);
229 }
230 }
231};
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)
std::string value
The raw ODBC connection string value.
LIGHTWEIGHT_API std::string Sanitized() const
Returns a copy of the connection string with every password value masked, see SanitizePwd.