5#if defined(_WIN32) || defined(_WIN64)
10#include "SqlConnection.hpp"
11#include "SqlDataBinder.hpp"
12#include "SqlQuery.hpp"
13#include "SqlServerType.hpp"
20#include <source_location>
34template <
typename QueryObject>
36 { queryObject.ToSql() } -> std::convertible_to<std::string>;
40class SqlVariantRowCursor;
70 [[nodiscard]] LIGHTWEIGHT_API
bool IsAlive() const noexcept;
72 [[nodiscard]] LIGHTWEIGHT_API
bool IsPrepared() const noexcept;
75 [[nodiscard]] LIGHTWEIGHT_API
SqlConnection& Connection() noexcept;
78 [[nodiscard]] LIGHTWEIGHT_API
SqlConnection const& Connection() const noexcept;
81 [[nodiscard]] LIGHTWEIGHT_API
SqlErrorInfo LastError() const;
88 std::string_view
const& tableAlias)
const;
91 [[nodiscard]] LIGHTWEIGHT_API SQLHSTMT NativeHandle() const noexcept;
97 LIGHTWEIGHT_API
void Prepare(std::string_view query) &;
99 LIGHTWEIGHT_API
SqlStatement Prepare(std::string_view query) &&;
109 [[nodiscard]] std::
string const& PreparedQuery() const noexcept;
111 template <SqlInputParameterBinder Arg>
112 void BindInputParameter(SQLSMALLINT columnIndex, Arg const& arg);
114 template <SqlInputParameterBinder Arg, typename ColumnName>
115 void BindInputParameter(SQLSMALLINT columnIndex, Arg const& arg, ColumnName&& columnNameHint);
120 template <SqlOutputColumnBinder... Args>
121 void BindOutputColumns(Args*... args);
129 template <typename... Records>
130 requires(((std::is_class_v<Records> && std::is_aggregate_v<Records>) && ...))
131 void BindOutputColumnsToRecord(Records*... records);
133 template <SqlOutputColumnBinder T>
134 void BindOutputColumn(SQLUSMALLINT columnIndex, T* arg);
137 template <SqlInputParameterBinder... Args>
138 void Execute(Args const&... args);
141 LIGHTWEIGHT_API
void ExecuteWithVariants(std::vector<
SqlVariant> const& args);
151 template <SqlInputParameterBatchBinder FirstColumnBatch, std::ranges::contiguous_range... MoreColumnBatches>
152 void ExecuteBatchNative(FirstColumnBatch const& firstColumnBatch, MoreColumnBatches const&... moreColumnBatches);
162 template <SqlInputParameterBatchBinder FirstColumnBatch, std::ranges::range... MoreColumnBatches>
163 void ExecuteBatchSoft(FirstColumnBatch const& firstColumnBatch, MoreColumnBatches const&... moreColumnBatches);
170 template <SqlInputParameterBatchBinder FirstColumnBatch, std::ranges::range... MoreColumnBatches>
171 void ExecuteBatch(FirstColumnBatch const& firstColumnBatch, MoreColumnBatches const&... moreColumnBatches);
174 LIGHTWEIGHT_API
void ExecuteDirect(std::string_view const& query,
175 std::source_location location = std::source_location::current());
178 void ExecuteDirect(
SqlQueryObject auto const& query, std::source_location location = std::source_location::current());
181 template <typename Callable>
183 void MigrateDirect(Callable const& callable, std::source_location location = std::source_location::current());
187 template <typename T>
189 [[nodiscard]] std::optional<T> ExecuteDirectScalar(std::string_view const& query,
190 std::source_location location = std::source_location::current());
192 template <typename T>
194 [[nodiscard]] T ExecuteDirectScalar(std::string_view const& query,
195 std::source_location location = std::source_location::current());
199 template <typename T>
201 [[nodiscard]] std::optional<T> ExecuteDirectScalar(
SqlQueryObject auto const& query,
202 std::source_location location = std::source_location::current());
204 template <typename T>
206 [[nodiscard]] T ExecuteDirectScalar(
SqlQueryObject auto const& query,
207 std::source_location location = std::source_location::current());
210 [[nodiscard]] LIGHTWEIGHT_API
size_t NumRowsAffected() const;
213 [[nodiscard]] LIGHTWEIGHT_API
size_t NumColumnsAffected() const;
216 [[nodiscard]] LIGHTWEIGHT_API
size_t LastInsertId(std::string_view tableName);
224 [[nodiscard]] LIGHTWEIGHT_API
bool FetchRow();
226 [[nodiscard]] LIGHTWEIGHT_API std::expected<
bool,
SqlErrorInfo> TryFetchRow(
227 std::source_location location = std::source_location::current()) noexcept;
232 void CloseCursor() noexcept;
238 SqlVariantRowCursor GetVariantRowCursor() noexcept;
243 template <SqlGetColumnNativeType T>
244 [[nodiscard]]
bool GetColumn(SQLUSMALLINT column, T* result) const;
247 template <SqlGetColumnNativeType T>
248 [[nodiscard]] T GetColumn(SQLUSMALLINT column) const;
253 template <SqlGetColumnNativeType T>
254 [[nodiscard]] std::optional<T> GetNullableColumn(SQLUSMALLINT column) const;
259 template <SqlGetColumnNativeType T>
260 [[nodiscard]] T GetColumnOr(SQLUSMALLINT column, T&& defaultValue) const;
263 LIGHTWEIGHT_API
void RequireSuccess(SQLRETURN error,
264 std::source_location sourceLocation = std::source_location::current()) const;
265 LIGHTWEIGHT_API
void PlanPostExecuteCallback(std::function<
void()>&& cb) override;
266 LIGHTWEIGHT_API
void PlanPostProcessOutputColumn(std::function<
void()>&& cb) override;
267 [[nodiscard]] LIGHTWEIGHT_API SqlServerType ServerType() const noexcept override;
268 [[nodiscard]] LIGHTWEIGHT_API std::
string const& DriverName() const noexcept override;
269 LIGHTWEIGHT_API
void ProcessPostExecuteCallbacks();
271 LIGHTWEIGHT_API
void RequireIndicators();
272 LIGHTWEIGHT_API SQLLEN* GetIndicatorForColumn(SQLUSMALLINT column) noexcept;
276 std::unique_ptr<Data,
void (*)(Data*)> m_data;
279 std::string m_preparedQuery;
280 std::optional<SQLSMALLINT> m_numColumns;
281 SQLSMALLINT m_expectedParameterCount {};
298 m_stmt { other.m_stmt }
300 other.m_stmt =
nullptr;
307 m_stmt = other.m_stmt;
308 other.m_stmt =
nullptr;
317 m_stmt->CloseCursor();
325 return m_stmt->NumRowsAffected();
331 return m_stmt->NumColumnsAffected();
337 template <SqlOutputColumnBinder... Args>
340 m_stmt->BindOutputColumns(args...);
343 template <SqlOutputColumnBinder T>
344 LIGHTWEIGHT_FORCE_INLINE
void BindOutputColumn(SQLUSMALLINT columnIndex, T* arg)
346 m_stmt->BindOutputColumn(columnIndex, arg);
350 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
bool FetchRow()
352 return m_stmt->FetchRow();
358 template <SqlGetColumnNativeType T>
359 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
bool GetColumn(SQLUSMALLINT column, T* result)
const
361 return m_stmt->GetColumn<T>(column, result);
365 template <SqlGetColumnNativeType T>
366 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE T
GetColumn(SQLUSMALLINT column)
const
368 return m_stmt->GetColumn<T>(column);
374 template <SqlGetColumnNativeType T>
375 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE std::optional<T>
GetNullableColumn(SQLUSMALLINT column)
const
377 return m_stmt->GetNullableColumn<T>(column);
383 template <SqlGetColumnNativeType T>
384 [[nodiscard]] T
GetColumnOr(SQLUSMALLINT column, T&& defaultValue)
const
386 return m_stmt->GetColumnOr(column, std::forward<T>(defaultValue));
393struct [[nodiscard]] SqlSentinelIterator
397class [[nodiscard]] SqlVariantRowIterator
400 explicit SqlVariantRowIterator(SqlSentinelIterator )
noexcept:
405 explicit SqlVariantRowIterator(SqlResultCursor& cursor)
noexcept:
406 _numResultColumns {
static_cast<SQLUSMALLINT
>(cursor.NumColumnsAffected()) },
409 _row.reserve(_numResultColumns);
413 SqlVariantRow& operator*() noexcept
418 SqlVariantRow
const& operator*() const noexcept
423 SqlVariantRowIterator& operator++() noexcept
425 _end = !_cursor->FetchRow();
429 for (
auto const i: std::views::iota(SQLUSMALLINT(1), SQLUSMALLINT(_numResultColumns + 1)))
430 _row.emplace_back(_cursor->GetColumn<SqlVariant>(i));
435 bool operator!=(SqlSentinelIterator )
const noexcept
440 bool operator!=(SqlVariantRowIterator
const& )
const noexcept
447 SQLUSMALLINT _numResultColumns = 0;
448 SqlResultCursor* _cursor;
452class [[nodiscard]] SqlVariantRowCursor
455 explicit SqlVariantRowCursor(SqlResultCursor&& cursor):
456 _resultCursor { std::move(cursor) }
460 SqlVariantRowIterator begin() noexcept
462 return SqlVariantRowIterator { _resultCursor };
465 static SqlSentinelIterator end() noexcept
467 return SqlSentinelIterator {};
471 SqlResultCursor _resultCursor;
498 _connection { &conn }
505 using difference_type = bool;
506 using value_type = T;
508 iterator& operator++()
510 _is_end = !_stmt->FetchRow();
516 _stmt->CloseCursor();
521 LIGHTWEIGHT_FORCE_INLINE value_type operator*()
noexcept
525 Reflection::EnumerateMembers(res, [
this]<
size_t I>(
auto&& value) {
526 auto tmp = _stmt->GetColumn<
typename Reflection::MemberTypeOf<I, value_type>::ValueType>(I + 1);
533 LIGHTWEIGHT_FORCE_INLINE
constexpr bool operator!=(iterator
const& other)
const noexcept
535 return _is_end != other._is_end;
538 constexpr iterator(std::default_sentinel_t )
noexcept:
544 _stmt { std::make_unique<SqlStatement>(conn) }
548 LIGHTWEIGHT_FORCE_INLINE
SqlStatement& Statement()
noexcept
554 bool _is_end =
false;
555 std::unique_ptr<SqlStatement> _stmt;
560 auto it = iterator { *_connection };
561 auto& stmt = it.Statement();
562 stmt.
Prepare(it.Statement().Query(RecordTableName<T>).Select().template Fields<T>().All());
568 iterator end()
noexcept
570 return iterator { std::default_sentinel };
578inline LIGHTWEIGHT_FORCE_INLINE
bool SqlStatement::IsAlive() const noexcept
580 return m_connection && m_connection->
IsAlive() && m_hStmt !=
nullptr;
583inline LIGHTWEIGHT_FORCE_INLINE
bool SqlStatement::IsPrepared() const noexcept
585 return !m_preparedQuery.empty();
590 return *m_connection;
595 return *m_connection;
610 Prepare(queryObject.ToSql());
615 return Prepare(queryObject.ToSql());
618inline LIGHTWEIGHT_FORCE_INLINE std::string
const& SqlStatement::PreparedQuery() const noexcept
620 return m_preparedQuery;
623template <SqlOutputColumnBinder... Args>
629 ((++i, RequireSuccess(SqlDataBinder<Args>::OutputColumn(m_hStmt, i, args, GetIndicatorForColumn(i), *
this))), ...);
632template <
typename... Records>
633 requires(((std::is_class_v<Records> && std::is_aggregate_v<Records>) && ...))
639 ((Reflection::EnumerateMembers(*records,
640 [
this, &i]<
size_t I,
typename FieldType>(FieldType& value) {
642 RequireSuccess(SqlDataBinder<FieldType>::OutputColumn(
643 m_hStmt, i, &value, GetIndicatorForColumn(i), *
this));
648template <SqlOutputColumnBinder T>
649inline LIGHTWEIGHT_FORCE_INLINE
void SqlStatement::BindOutputColumn(SQLUSMALLINT columnIndex, T* arg)
653 RequireSuccess(SqlDataBinder<T>::OutputColumn(m_hStmt, columnIndex, arg, GetIndicatorForColumn(columnIndex), *
this));
656template <SqlInputParameterBinder Arg>
657inline LIGHTWEIGHT_FORCE_INLINE
void SqlStatement::BindInputParameter(SQLSMALLINT columnIndex, Arg
const& arg)
660 m_expectedParameterCount = (std::numeric_limits<
decltype(m_expectedParameterCount)>::max)();
661 RequireSuccess(SqlDataBinder<Arg>::InputParameter(m_hStmt, columnIndex, arg, *
this));
664template <SqlInputParameterBinder Arg,
typename ColumnName>
665inline LIGHTWEIGHT_FORCE_INLINE
void SqlStatement::BindInputParameter(SQLSMALLINT columnIndex,
667 ColumnName&& columnNameHint)
670 BindInputParameter(columnIndex, arg);
673template <SqlInputParameterBinder... Args>
682 if (!(m_expectedParameterCount == (std::numeric_limits<
decltype(m_expectedParameterCount)>::max)()
683 &&
sizeof...(args) == 0)
684 && !(m_expectedParameterCount ==
sizeof...(args)))
685 throw std::invalid_argument {
"Invalid argument count" };
690 RequireSuccess(SqlDataBinder<Args>::InputParameter(m_hStmt, i, args, *
this))),
693 auto const result = SQLExecute(m_hStmt);
695 if (result != SQL_NO_DATA && result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO)
698 ProcessPostExecuteCallbacks();
703concept SqlNativeContiguousValueConcept =
704 std::same_as<T, bool>
705 || std::same_as<T, char>
706 || std::same_as<T, unsigned char>
707 || std::same_as<T, wchar_t>
708 || std::same_as<T, std::int16_t>
709 || std::same_as<T, std::uint16_t>
710 || std::same_as<T, std::int32_t>
711 || std::same_as<T, std::uint32_t>
712 || std::same_as<T, std::int64_t>
713 || std::same_as<T, std::uint64_t>
714 || std::same_as<T, float>
715 || std::same_as<T, double>
716 || std::same_as<T, SqlDate>
717 || std::same_as<T, SqlTime>
718 || std::same_as<T, SqlDateTime>
719 || std::same_as<T, SqlFixedString<T::Capacity, typename T::value_type, T::PostRetrieveOperation>>;
721template <
typename FirstColumnBatch,
typename... MoreColumnBatches>
722concept SqlNativeBatchable =
723 std::ranges::contiguous_range<FirstColumnBatch>
724 && (std::ranges::contiguous_range<MoreColumnBatches> && ...)
725 && SqlNativeContiguousValueConcept<std::ranges::range_value_t<FirstColumnBatch>>
726 && (SqlNativeContiguousValueConcept<std::ranges::range_value_t<MoreColumnBatches>> && ...);
730template <SqlInputParameterBatchBinder FirstColumnBatch, std::ranges::contiguous_range... MoreColumnBatches>
732 MoreColumnBatches
const&... moreColumnBatches)
734 static_assert(SqlNativeBatchable<FirstColumnBatch, MoreColumnBatches...>,
735 "Must be a supported native contiguous element type.");
737 if (m_expectedParameterCount != 1 +
sizeof...(moreColumnBatches))
738 throw std::invalid_argument {
"Invalid number of columns" };
740 auto const rowCount = std::ranges::size(firstColumnBatch);
741 if (!((std::size(moreColumnBatches) == rowCount) && ...))
742 throw std::invalid_argument {
"Uneven number of rows" };
748 RequireSuccess(SQLSetStmtAttr(m_hStmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER) rowCount, 0));
749 RequireSuccess(SQLSetStmtAttr(m_hStmt, SQL_ATTR_PARAM_BIND_OFFSET_PTR, &rowStart, 0));
750 RequireSuccess(SQLSetStmtAttr(m_hStmt, SQL_ATTR_PARAM_BIND_TYPE, SQL_PARAM_BIND_BY_COLUMN, 0));
751 RequireSuccess(SQLSetStmtAttr(m_hStmt, SQL_ATTR_PARAM_OPERATION_PTR, SQL_PARAM_PROCEED, 0));
752 RequireSuccess(SqlDataBinder<std::remove_cvref_t<
decltype(*std::ranges::data(firstColumnBatch))>>::
753 InputParameter(m_hStmt, 1, *std::ranges::data(firstColumnBatch), *
this));
754 SQLUSMALLINT column = 1;
755 (RequireSuccess(SqlDataBinder<std::remove_cvref_t<
decltype(*std::ranges::data(moreColumnBatches))>>::
756 InputParameter(m_hStmt, ++column, *std::ranges::data(moreColumnBatches), *
this)), ...);
757 RequireSuccess(SQLExecute(m_hStmt));
758 ProcessPostExecuteCallbacks();
762template <SqlInputParameterBatchBinder FirstColumnBatch, std::ranges::range... MoreColumnBatches>
764 MoreColumnBatches
const&... moreColumnBatches)
768 if constexpr (SqlNativeBatchable<FirstColumnBatch, MoreColumnBatches...>)
774template <SqlInputParameterBatchBinder FirstColumnBatch, std::ranges::range... MoreColumnBatches>
777 if (m_expectedParameterCount != 1 +
sizeof...(moreColumnBatches))
778 throw std::invalid_argument {
"Invalid number of columns" };
780 auto const rowCount = std::ranges::size(firstColumnBatch);
781 if (!((std::size(moreColumnBatches) == rowCount) && ...))
782 throw std::invalid_argument {
"Uneven number of rows" };
784 for (
auto const rowIndex: std::views::iota(
size_t { 0 }, rowCount))
787 [&]<SqlInputParameterBinder... ColumnValues>(ColumnValues
const&... columnsInRow) {
788 SQLUSMALLINT column = 0;
789 ((++column, SqlDataBinder<ColumnValues>::InputParameter(m_hStmt, column, columnsInRow, *this)), ...);
790 RequireSuccess(SQLExecute(m_hStmt));
791 ProcessPostExecuteCallbacks();
793 std::make_tuple(std::ref(*std::ranges::next(std::ranges::begin(firstColumnBatch), rowIndex)),
794 std::ref(*std::ranges::next(std::ranges::begin(moreColumnBatches), rowIndex))...));
798template <SqlGetColumnNativeType T>
802 RequireSuccess(SqlDataBinder<T>::GetColumn(m_hStmt, column, result, &indicator, *
this));
803 return indicator != SQL_NULL_DATA;
809 template <
typename T>
810 concept SqlNullableType = (std::same_as<T, SqlVariant> || IsSpecializationOf<std::optional, T>);
814template <SqlGetColumnNativeType T>
819 RequireSuccess(SqlDataBinder<T>::GetColumn(m_hStmt, column, &result, &indicator, *
this));
820 if constexpr (!detail::SqlNullableType<T>)
821 if (indicator == SQL_NULL_DATA)
822 throw std::runtime_error {
"Column value is NULL" };
826template <SqlGetColumnNativeType T>
831 RequireSuccess(SqlDataBinder<T>::GetColumn(m_hStmt, column, &result, &indicator, *
this));
832 if (indicator == SQL_NULL_DATA)
834 return { std::move(result) };
837template <SqlGetColumnNativeType T>
840 return GetNullableColumn<T>(column).value_or(std::forward<T>(defaultValue));
844 std::source_location location)
849template <
typename Callable>
850 requires std::invocable<Callable, SqlMigrationQueryBuilder&>
855 auto const queries = migration.GetPlan().ToSql();
856 for (
auto const& query: queries)
864 requires(!std::same_as<T, SqlVariant>)
867 auto const _ = detail::Finally([
this] { CloseCursor(); });
868 ExecuteDirect(query, location);
869 RequireSuccess(FetchRow());
870 return GetNullableColumn<T>(1);
874 requires(std::same_as<T, SqlVariant>)
877 auto const _ = detail::Finally([
this] { CloseCursor(); });
878 ExecuteDirect(query, location);
879 RequireSuccess(FetchRow());
880 if (
auto result = GetNullableColumn<T>(1); result.has_value())
886 requires(!std::same_as<T, SqlVariant>)
889 return ExecuteDirectScalar<T>(query.ToSql(), location);
893 requires(std::same_as<T, SqlVariant>)
896 return ExecuteDirectScalar<T>(query.ToSql(), location);
902 SQLFreeStmt(m_hStmt, SQL_CLOSE);
Represents a connection to a SQL database.
LIGHTWEIGHT_API bool IsAlive() const noexcept
Tests if the connection is still active.
SqlQueryFormatter const & QueryFormatter() const noexcept
Retrieves a query formatter suitable for the SQL server being connected.
virtual void OnExecute(std::string_view const &query)=0
Invoked when a prepared query is executed.
static LIGHTWEIGHT_API SqlLogger & GetLogger()
Retrieves the currently configured logger.
virtual void OnFetchEnd()=0
Invoked when fetching is done.
void OnBindInputParameter(std::string_view const &name, T &&value)
Invoked when an input parameter is bound.
Query builder for building SQL migration queries.
API Entry point for building SQL queries.
API for reading an SQL query result set.
LIGHTWEIGHT_FORCE_INLINE bool GetColumn(SQLUSMALLINT column, T *result) const
LIGHTWEIGHT_FORCE_INLINE void BindOutputColumns(Args *... args)
LIGHTWEIGHT_FORCE_INLINE std::optional< T > GetNullableColumn(SQLUSMALLINT column) const
LIGHTWEIGHT_FORCE_INLINE T GetColumn(SQLUSMALLINT column) const
Retrieves the value of the column at the given index for the currently selected row.
T GetColumnOr(SQLUSMALLINT column, T &&defaultValue) const
LIGHTWEIGHT_FORCE_INLINE size_t NumColumnsAffected() const
Retrieves the number of columns affected by the last query.
LIGHTWEIGHT_FORCE_INLINE size_t NumRowsAffected() const
Retrieves the number of rows affected by the last query.
LIGHTWEIGHT_FORCE_INLINE bool FetchRow()
Fetches the next row of the result set.
SQL query result row iterator.
High level API for (prepared) raw SQL statements.
LIGHTWEIGHT_API void Prepare(std::string_view query) &
LIGHTWEIGHT_API SQLHSTMT NativeHandle() const noexcept
Retrieves the native handle of the statement.
LIGHTWEIGHT_API SqlQueryBuilder QueryAs(std::string_view const &table, std::string_view const &tableAlias) const
Creates a new query builder for the given table with an alias, compatible with the SQL server being c...
void MigrateDirect(Callable const &callable, std::source_location location=std::source_location::current())
Executes an SQL migration query, as created b the callback.
LIGHTWEIGHT_API SqlConnection & Connection() noexcept
Retrieves the connection associated with this statement.
void ExecuteBatch(FirstColumnBatch const &firstColumnBatch, MoreColumnBatches const &... moreColumnBatches)
void ExecuteBatchSoft(FirstColumnBatch const &firstColumnBatch, MoreColumnBatches const &... moreColumnBatches)
void CloseCursor() noexcept
void Execute(Args const &... args)
Binds the given arguments to the prepared statement and executes it.
void ExecuteBatchNative(FirstColumnBatch const &firstColumnBatch, MoreColumnBatches const &... moreColumnBatches)
LIGHTWEIGHT_API SqlErrorInfo LastError() const
Retrieves the last error information with respect to this SQL statement handle.
SqlVariantRowCursor GetVariantRowCursor() noexcept
Retrieves the variant row cursor for reading an SQL query result of unknown column types and column c...
SqlResultCursor GetResultCursor() noexcept
Retrieves the result cursor for reading an SQL query result.
bool GetColumn(SQLUSMALLINT column, T *result) const
LIGHTWEIGHT_API SqlStatement()
Construct a new SqlStatement object, using a new connection, and connect to the default database.
void BindOutputColumnsToRecord(Records *... records)
T GetColumnOr(SQLUSMALLINT column, T &&defaultValue) const
LIGHTWEIGHT_API SqlStatement(std::nullopt_t)
Construct a new empty SqlStatement object. No SqlConnection is associated with this statement.
std::optional< T > ExecuteDirectScalar(std::string_view const &query, std::source_location location=std::source_location::current())
LIGHTWEIGHT_API void ExecuteDirect(std::string_view const &query, std::source_location location=std::source_location::current())
Executes the given query directly.
std::optional< T > GetNullableColumn(SQLUSMALLINT column) const
LIGHTWEIGHT_API SqlStatement(SqlConnection &relatedConnection)
Construct a new SqlStatement object, using the given connection.
void BindOutputColumns(Args *... args)
Represents an SQL query object, that provides a ToSql() method.
constexpr auto SqlNullValue
Represents an ODBC SQL error.
static SqlErrorInfo FromStatementHandle(SQLHSTMT hStmt)
Constructs an ODBC error info object from the given ODBC statement handle.
Represents a value that can be any of the supported SQL data types.