6#if defined(_WIN32) || defined(_WIN64)
11#include "SqlConnection.hpp"
12#include "SqlDataBinder.hpp"
13#include "SqlQuery.hpp"
14#include "SqlServerType.hpp"
21#include <source_location>
31template <
typename QueryObject>
33 { queryObject.ToSql() } -> std::convertible_to<std::string>;
37class SqlVariantRowCursor;
67 [[nodiscard]] LIGHTWEIGHT_API
bool IsAlive() const noexcept;
69 [[nodiscard]] LIGHTWEIGHT_API
bool IsPrepared() const noexcept;
72 [[nodiscard]] LIGHTWEIGHT_API
SqlConnection& Connection() noexcept;
75 [[nodiscard]] LIGHTWEIGHT_API
SqlConnection const& Connection() const noexcept;
78 [[nodiscard]] LIGHTWEIGHT_API
SqlErrorInfo LastError() const;
85 std::string_view
const& tableAlias)
const;
88 [[nodiscard]] LIGHTWEIGHT_API SQLHSTMT NativeHandle() const noexcept;
94 LIGHTWEIGHT_API
void Prepare(std::string_view query) &;
96 LIGHTWEIGHT_API
SqlStatement Prepare(std::string_view query) &&;
106 [[nodiscard]] std::
string const& PreparedQuery() const noexcept;
108 template <SqlInputParameterBinder Arg>
109 void BindInputParameter(SQLSMALLINT columnIndex, Arg const& arg);
111 template <SqlInputParameterBinder Arg, typename ColumnName>
112 void BindInputParameter(SQLSMALLINT columnIndex, Arg const& arg, ColumnName&& columnNameHint);
117 template <SqlOutputColumnBinder... Args>
118 void BindOutputColumns(Args*... args);
126 template <typename... Records>
127 requires(((std::is_class_v<Records> && std::is_aggregate_v<Records>) && ...))
128 void BindOutputColumnsToRecord(Records*... records);
130 template <SqlOutputColumnBinder T>
131 void BindOutputColumn(SQLUSMALLINT columnIndex, T* arg);
134 template <SqlInputParameterBinder... Args>
135 void Execute(Args const&... args);
138 LIGHTWEIGHT_API
void ExecuteWithVariants(std::vector<
SqlVariant> const& args);
148 template <SqlInputParameterBatchBinder FirstColumnBatch, std::ranges::contiguous_range... MoreColumnBatches>
149 void ExecuteBatchNative(FirstColumnBatch const& firstColumnBatch, MoreColumnBatches const&... moreColumnBatches);
159 template <SqlInputParameterBatchBinder FirstColumnBatch, std::ranges::range... MoreColumnBatches>
160 void ExecuteBatchSoft(FirstColumnBatch const& firstColumnBatch, MoreColumnBatches const&... moreColumnBatches);
167 template <SqlInputParameterBatchBinder FirstColumnBatch, std::ranges::range... MoreColumnBatches>
168 void ExecuteBatch(FirstColumnBatch const& firstColumnBatch, MoreColumnBatches const&... moreColumnBatches);
171 LIGHTWEIGHT_API
void ExecuteDirect(std::string_view const& query,
172 std::source_location location = std::source_location::current());
175 void ExecuteDirect(
SqlQueryObject auto const& query, std::source_location location = std::source_location::current());
178 template <typename Callable>
180 void MigrateDirect(Callable const& callable, std::source_location location = std::source_location::current());
184 template <typename T>
186 [[nodiscard]] std::optional<T> ExecuteDirectScalar(std::string_view const& query,
187 std::source_location location = std::source_location::current());
189 template <typename T>
191 [[nodiscard]] T ExecuteDirectScalar(std::string_view const& query,
192 std::source_location location = std::source_location::current());
196 template <typename T>
198 [[nodiscard]] std::optional<T> ExecuteDirectScalar(
SqlQueryObject auto const& query,
199 std::source_location location = std::source_location::current());
201 template <typename T>
203 [[nodiscard]] T ExecuteDirectScalar(
SqlQueryObject auto const& query,
204 std::source_location location = std::source_location::current());
207 [[nodiscard]] LIGHTWEIGHT_API
size_t NumRowsAffected() const;
210 [[nodiscard]] LIGHTWEIGHT_API
size_t NumColumnsAffected() const;
213 [[nodiscard]] LIGHTWEIGHT_API
size_t LastInsertId(std::string_view tableName);
221 [[nodiscard]] LIGHTWEIGHT_API
bool FetchRow();
223 [[nodiscard]] LIGHTWEIGHT_API std::expected<
bool,
SqlErrorInfo> TryFetchRow(
224 std::source_location location = std::source_location::current()) noexcept;
229 void CloseCursor() noexcept;
235 SqlVariantRowCursor GetVariantRowCursor() noexcept;
240 template <SqlGetColumnNativeType T>
241 [[nodiscard]]
bool GetColumn(SQLUSMALLINT column, T* result) const;
244 template <SqlGetColumnNativeType T>
245 [[nodiscard]] T GetColumn(SQLUSMALLINT column) const;
250 template <SqlGetColumnNativeType T>
251 [[nodiscard]] std::optional<T> GetNullableColumn(SQLUSMALLINT column) const;
254 LIGHTWEIGHT_API
void RequireSuccess(SQLRETURN error,
255 std::source_location sourceLocation = std::source_location::current()) const;
256 LIGHTWEIGHT_API
void PlanPostExecuteCallback(std::function<
void()>&& cb) override;
257 LIGHTWEIGHT_API
void PlanPostProcessOutputColumn(std::function<
void()>&& cb) override;
258 [[nodiscard]] LIGHTWEIGHT_API SqlServerType ServerType() const noexcept override;
259 [[nodiscard]] LIGHTWEIGHT_API std::
string const& DriverName() const noexcept override;
260 LIGHTWEIGHT_API
void ProcessPostExecuteCallbacks();
262 LIGHTWEIGHT_API
void RequireIndicators();
263 LIGHTWEIGHT_API SQLLEN* GetIndicatorForColumn(SQLUSMALLINT column) noexcept;
267 std::unique_ptr<Data,
void (*)(Data*)> m_data;
270 std::string m_preparedQuery;
271 SQLSMALLINT m_expectedParameterCount {};
288 m_stmt { other.m_stmt }
290 other.m_stmt =
nullptr;
297 m_stmt = other.m_stmt;
298 other.m_stmt =
nullptr;
306 SQLCloseCursor(m_stmt->NativeHandle());
312 return m_stmt->NumRowsAffected();
318 return m_stmt->NumColumnsAffected();
324 template <SqlOutputColumnBinder... Args>
327 m_stmt->BindOutputColumns(args...);
330 template <SqlOutputColumnBinder T>
331 LIGHTWEIGHT_FORCE_INLINE
void BindOutputColumn(SQLUSMALLINT columnIndex, T* arg)
333 m_stmt->BindOutputColumn(columnIndex, arg);
337 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
bool FetchRow()
339 return m_stmt->FetchRow();
345 template <SqlGetColumnNativeType T>
346 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
bool GetColumn(SQLUSMALLINT column, T* result)
const
348 return m_stmt->GetColumn<T>(column, result);
352 template <SqlGetColumnNativeType T>
353 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE T
GetColumn(SQLUSMALLINT column)
const
355 return m_stmt->GetColumn<T>(column);
361 template <SqlGetColumnNativeType T>
362 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE std::optional<T>
GetNullableColumn(SQLUSMALLINT column)
const
364 return m_stmt->GetNullableColumn<T>(column);
371struct [[nodiscard]] SqlSentinelIterator
375class [[nodiscard]] SqlVariantRowIterator
378 explicit SqlVariantRowIterator(SqlSentinelIterator )
noexcept:
384 _numResultColumns {
static_cast<SQLUSMALLINT
>(cursor.NumColumnsAffected()) },
387 _row.reserve(_numResultColumns);
391 SqlVariantRow& operator*() noexcept
396 SqlVariantRow
const& operator*() const noexcept
401 SqlVariantRowIterator& operator++() noexcept
407 for (
auto const i: std::views::iota(SQLUSMALLINT(1), SQLUSMALLINT(_numResultColumns + 1)))
408 _row.emplace_back(_cursor->GetColumn<
SqlVariant>(i));
413 bool operator!=(SqlSentinelIterator )
const noexcept
418 bool operator!=(SqlVariantRowIterator
const& )
const noexcept
425 SQLUSMALLINT _numResultColumns = 0;
430class [[nodiscard]] SqlVariantRowCursor
434 _resultCursor { std::move(cursor) }
438 SqlVariantRowIterator begin() noexcept
440 return SqlVariantRowIterator { _resultCursor };
443 static SqlSentinelIterator end() noexcept
445 return SqlSentinelIterator {};
476 _connection { &conn }
483 using difference_type = bool;
484 using value_type = T;
486 iterator& operator++()
488 _is_end = !_stmt->FetchRow();
494 _stmt->CloseCursor();
499 LIGHTWEIGHT_FORCE_INLINE value_type operator*()
noexcept
503 Reflection::EnumerateMembers(res, [
this]<
size_t I>(
auto&& value) {
504 auto tmp = _stmt->GetColumn<
typename Reflection::MemberTypeOf<I, value_type>::ValueType>(I + 1);
511 LIGHTWEIGHT_FORCE_INLINE
constexpr bool operator!=(iterator
const& other)
const noexcept
513 return _is_end != other._is_end;
516 constexpr iterator(std::default_sentinel_t )
noexcept:
522 _stmt { std::make_unique<SqlStatement>(conn) }
526 LIGHTWEIGHT_FORCE_INLINE
SqlStatement& Statement()
noexcept
532 bool _is_end =
false;
533 std::unique_ptr<SqlStatement> _stmt;
538 auto it = iterator { *_connection };
539 auto& stmt = it.Statement();
540 stmt.
Prepare(it.Statement().Query(RecordTableName<T>).Select().template Fields<T>().All());
546 iterator end()
noexcept
548 return iterator { std::default_sentinel };
556inline LIGHTWEIGHT_FORCE_INLINE
bool SqlStatement::IsAlive() const noexcept
558 return m_connection && m_connection->
IsAlive() && m_hStmt !=
nullptr;
561inline LIGHTWEIGHT_FORCE_INLINE
bool SqlStatement::IsPrepared() const noexcept
563 return !m_preparedQuery.empty();
568 return *m_connection;
573 return *m_connection;
588 Prepare(queryObject.ToSql());
593 return Prepare(queryObject.ToSql());
596inline LIGHTWEIGHT_FORCE_INLINE std::string
const& SqlStatement::PreparedQuery() const noexcept
598 return m_preparedQuery;
601template <SqlOutputColumnBinder... Args>
607 ((++i, RequireSuccess(SqlDataBinder<Args>::OutputColumn(m_hStmt, i, args, GetIndicatorForColumn(i), *
this))), ...);
610template <
typename... Records>
611 requires(((std::is_class_v<Records> && std::is_aggregate_v<Records>) && ...))
617 ((Reflection::EnumerateMembers(*records,
618 [
this, &i]<
size_t I,
typename FieldType>(FieldType& value) {
620 RequireSuccess(SqlDataBinder<FieldType>::OutputColumn(
621 m_hStmt, i, &value, GetIndicatorForColumn(i), *
this));
626template <SqlOutputColumnBinder T>
627inline LIGHTWEIGHT_FORCE_INLINE
void SqlStatement::BindOutputColumn(SQLUSMALLINT columnIndex, T* arg)
631 RequireSuccess(SqlDataBinder<T>::OutputColumn(m_hStmt, columnIndex, arg, GetIndicatorForColumn(columnIndex), *
this));
634template <SqlInputParameterBinder Arg>
635inline LIGHTWEIGHT_FORCE_INLINE
void SqlStatement::BindInputParameter(SQLSMALLINT columnIndex, Arg
const& arg)
638 m_expectedParameterCount = (std::numeric_limits<
decltype(m_expectedParameterCount)>::max)();
639 RequireSuccess(SqlDataBinder<Arg>::InputParameter(m_hStmt, columnIndex, arg, *
this));
642template <SqlInputParameterBinder Arg,
typename ColumnName>
643inline LIGHTWEIGHT_FORCE_INLINE
void SqlStatement::BindInputParameter(SQLSMALLINT columnIndex,
645 ColumnName&& columnNameHint)
648 BindInputParameter(columnIndex, arg);
651template <SqlInputParameterBinder... Args>
660 if (!(m_expectedParameterCount == (std::numeric_limits<
decltype(m_expectedParameterCount)>::max)()
661 &&
sizeof...(args) == 0)
662 && !(m_expectedParameterCount ==
sizeof...(args)))
663 throw std::invalid_argument {
"Invalid argument count" };
668 RequireSuccess(SqlDataBinder<Args>::InputParameter(m_hStmt, i, args, *
this))),
671 auto const result = SQLExecute(m_hStmt);
673 if (result != SQL_NO_DATA && result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO)
676 ProcessPostExecuteCallbacks();
681concept SqlNativeContiguousValueConcept =
682 std::same_as<T, bool>
683 || std::same_as<T, char>
684 || std::same_as<T, unsigned char>
685 || std::same_as<T, wchar_t>
686 || std::same_as<T, std::int16_t>
687 || std::same_as<T, std::uint16_t>
688 || std::same_as<T, std::int32_t>
689 || std::same_as<T, std::uint32_t>
690 || std::same_as<T, std::int64_t>
691 || std::same_as<T, std::uint64_t>
692 || std::same_as<T, float>
693 || std::same_as<T, double>
694 || std::same_as<T, SqlDate>
695 || std::same_as<T, SqlTime>
696 || std::same_as<T, SqlDateTime>
697 || std::same_as<T, SqlFixedString<T::Capacity, typename T::value_type, T::PostRetrieveOperation>>;
699template <
typename FirstColumnBatch,
typename... MoreColumnBatches>
700concept SqlNativeBatchable =
701 std::ranges::contiguous_range<FirstColumnBatch>
702 && (std::ranges::contiguous_range<MoreColumnBatches> && ...)
703 && SqlNativeContiguousValueConcept<std::ranges::range_value_t<FirstColumnBatch>>
704 && (SqlNativeContiguousValueConcept<std::ranges::range_value_t<MoreColumnBatches>> && ...);
708template <SqlInputParameterBatchBinder FirstColumnBatch, std::ranges::contiguous_range... MoreColumnBatches>
710 MoreColumnBatches
const&... moreColumnBatches)
712 static_assert(SqlNativeBatchable<FirstColumnBatch, MoreColumnBatches...>,
713 "Must be a supported native contiguous element type.");
715 if (m_expectedParameterCount != 1 +
sizeof...(moreColumnBatches))
716 throw std::invalid_argument {
"Invalid number of columns" };
718 auto const rowCount = std::ranges::size(firstColumnBatch);
719 if (!((std::size(moreColumnBatches) == rowCount) && ...))
720 throw std::invalid_argument {
"Uneven number of rows" };
726 RequireSuccess(SQLSetStmtAttr(m_hStmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER) rowCount, 0));
727 RequireSuccess(SQLSetStmtAttr(m_hStmt, SQL_ATTR_PARAM_BIND_OFFSET_PTR, &rowStart, 0));
728 RequireSuccess(SQLSetStmtAttr(m_hStmt, SQL_ATTR_PARAM_BIND_TYPE, SQL_PARAM_BIND_BY_COLUMN, 0));
729 RequireSuccess(SQLSetStmtAttr(m_hStmt, SQL_ATTR_PARAM_OPERATION_PTR, SQL_PARAM_PROCEED, 0));
730 RequireSuccess(SqlDataBinder<std::remove_cvref_t<
decltype(*std::ranges::data(firstColumnBatch))>>::
731 InputParameter(m_hStmt, 1, *std::ranges::data(firstColumnBatch), *
this));
732 SQLUSMALLINT column = 1;
733 (RequireSuccess(SqlDataBinder<std::remove_cvref_t<
decltype(*std::ranges::data(moreColumnBatches))>>::
734 InputParameter(m_hStmt, ++column, *std::ranges::data(moreColumnBatches), *
this)), ...);
735 RequireSuccess(SQLExecute(m_hStmt));
736 ProcessPostExecuteCallbacks();
740template <SqlInputParameterBatchBinder FirstColumnBatch, std::ranges::range... MoreColumnBatches>
742 MoreColumnBatches
const&... moreColumnBatches)
746 if constexpr (SqlNativeBatchable<FirstColumnBatch, MoreColumnBatches...>)
752template <SqlInputParameterBatchBinder FirstColumnBatch, std::ranges::range... MoreColumnBatches>
755 if (m_expectedParameterCount != 1 +
sizeof...(moreColumnBatches))
756 throw std::invalid_argument {
"Invalid number of columns" };
758 auto const rowCount = std::ranges::size(firstColumnBatch);
759 if (!((std::size(moreColumnBatches) == rowCount) && ...))
760 throw std::invalid_argument {
"Uneven number of rows" };
762 for (
auto const rowIndex: std::views::iota(
size_t { 0 }, rowCount))
765 [&]<SqlInputParameterBinder... ColumnValues>(ColumnValues
const&... columnsInRow) {
766 SQLUSMALLINT column = 0;
767 ((++column, SqlDataBinder<ColumnValues>::InputParameter(m_hStmt, column, columnsInRow, *this)), ...);
768 RequireSuccess(SQLExecute(m_hStmt));
769 ProcessPostExecuteCallbacks();
771 std::make_tuple(std::ref(*std::ranges::next(std::ranges::begin(firstColumnBatch), rowIndex)),
772 std::ref(*std::ranges::next(std::ranges::begin(moreColumnBatches), rowIndex))...));
776template <SqlGetColumnNativeType T>
780 RequireSuccess(SqlDataBinder<T>::GetColumn(m_hStmt, column, result, &indicator, *
this));
781 return indicator != SQL_NULL_DATA;
788concept SqlNullableType = (std::same_as<T, SqlVariant> || IsSpecializationOf<std::optional, T>);
792template <SqlGetColumnNativeType T>
797 RequireSuccess(SqlDataBinder<T>::GetColumn(m_hStmt, column, &result, &indicator, *
this));
798 if constexpr (!detail::SqlNullableType<T>)
799 if (indicator == SQL_NULL_DATA)
800 throw std::runtime_error {
"Column value is NULL" };
804template <SqlGetColumnNativeType T>
809 RequireSuccess(SqlDataBinder<T>::GetColumn(m_hStmt, column, &result, &indicator, *
this));
810 if (indicator == SQL_NULL_DATA)
812 return { std::move(result) };
816 std::source_location location)
821template <
typename Callable>
822 requires std::invocable<Callable, SqlMigrationQueryBuilder&>
827 auto const queries = migration.GetPlan().ToSql();
828 for (
auto const& query: queries)
836 requires(!std::same_as<T, SqlVariant>)
839 auto const _ = detail::Finally([
this] { CloseCursor(); });
840 ExecuteDirect(query, location);
841 RequireSuccess(FetchRow());
842 return GetNullableColumn<T>(1);
846 requires(std::same_as<T, SqlVariant>)
849 auto const _ = detail::Finally([
this] { CloseCursor(); });
850 ExecuteDirect(query, location);
851 RequireSuccess(FetchRow());
852 if (
auto result = GetNullableColumn<T>(1); result.has_value())
858 requires(!std::same_as<T, SqlVariant>)
861 return ExecuteDirectScalar<T>(query.ToSql(), location);
865 requires(std::same_as<T, SqlVariant>)
868 return ExecuteDirectScalar<T>(query.ToSql(), location);
874 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.
void OnBindInputParameter(std::string_view const &name, T &&value)
Invoked when an input parameter is bound.
virtual void OnFetchEnd()=0
Invoked when fetching is done.
static SqlLogger & GetLogger()
Retrieves the currently configured logger.
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 T GetColumn(SQLUSMALLINT column) const
Retrieves the value of the column at the given index for the currently selected row.
LIGHTWEIGHT_FORCE_INLINE void BindOutputColumns(Args *... args)
LIGHTWEIGHT_FORCE_INLINE std::optional< T > GetNullableColumn(SQLUSMALLINT column) const
LIGHTWEIGHT_FORCE_INLINE bool FetchRow()
Fetches the next row of the result set.
LIGHTWEIGHT_FORCE_INLINE size_t NumColumnsAffected() const
Retrieves the number of columns affected by the last query.
LIGHTWEIGHT_FORCE_INLINE bool GetColumn(SQLUSMALLINT column, T *result) const
LIGHTWEIGHT_FORCE_INLINE size_t NumRowsAffected() const
Retrieves the number of rows affected by the last query.
SQL query result row iterator.
High level API for (prepared) raw SQL statements.
void ExecuteBatch(FirstColumnBatch const &firstColumnBatch, MoreColumnBatches const &... moreColumnBatches)
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 ExecuteBatchNative(FirstColumnBatch const &firstColumnBatch, MoreColumnBatches const &... moreColumnBatches)
LIGHTWEIGHT_API SqlConnection & Connection() noexcept
Retrieves the connection associated with this statement.
void BindOutputColumnsToRecord(Records *... records)
std::optional< T > ExecuteDirectScalar(std::string_view const &query, std::source_location location=std::source_location::current())
LIGHTWEIGHT_API SQLHSTMT NativeHandle() const noexcept
Retrieves the native handle of the statement.
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.
void Execute(Args const &... args)
Binds the given arguments to the prepared statement and executes it.
void CloseCursor() noexcept
LIGHTWEIGHT_API SqlErrorInfo LastError() const
Retrieves the last error information with respect to this SQL statement handle.
void ExecuteBatchSoft(FirstColumnBatch const &firstColumnBatch, MoreColumnBatches const &... moreColumnBatches)
LIGHTWEIGHT_API void ExecuteDirect(std::string_view const &query, std::source_location location=std::source_location::current())
Executes the given query directly.
void MigrateDirect(Callable const &callable, std::source_location location=std::source_location::current())
Executes an SQL migration query, as created b the callback.
std::optional< T > GetNullableColumn(SQLUSMALLINT column) const
LIGHTWEIGHT_API bool FetchRow()
void BindOutputColumns(Args *... args)
LIGHTWEIGHT_API void Prepare(std::string_view query) &
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.
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.