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 std::optional<SQLSMALLINT> m_numColumns;
272 SQLSMALLINT m_expectedParameterCount {};
289 m_stmt { other.m_stmt }
291 other.m_stmt =
nullptr;
298 m_stmt = other.m_stmt;
299 other.m_stmt =
nullptr;
308 m_stmt->CloseCursor();
316 return m_stmt->NumRowsAffected();
322 return m_stmt->NumColumnsAffected();
328 template <SqlOutputColumnBinder... Args>
331 m_stmt->BindOutputColumns(args...);
334 template <SqlOutputColumnBinder T>
335 LIGHTWEIGHT_FORCE_INLINE
void BindOutputColumn(SQLUSMALLINT columnIndex, T* arg)
337 m_stmt->BindOutputColumn(columnIndex, arg);
341 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
bool FetchRow()
343 return m_stmt->FetchRow();
349 template <SqlGetColumnNativeType T>
350 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE
bool GetColumn(SQLUSMALLINT column, T* result)
const
352 return m_stmt->GetColumn<T>(column, result);
356 template <SqlGetColumnNativeType T>
357 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE T
GetColumn(SQLUSMALLINT column)
const
359 return m_stmt->GetColumn<T>(column);
365 template <SqlGetColumnNativeType T>
366 [[nodiscard]] LIGHTWEIGHT_FORCE_INLINE std::optional<T>
GetNullableColumn(SQLUSMALLINT column)
const
368 return m_stmt->GetNullableColumn<T>(column);
375struct [[nodiscard]] SqlSentinelIterator
379class [[nodiscard]] SqlVariantRowIterator
382 explicit SqlVariantRowIterator(SqlSentinelIterator )
noexcept:
388 _numResultColumns {
static_cast<SQLUSMALLINT
>(cursor.NumColumnsAffected()) },
391 _row.reserve(_numResultColumns);
395 SqlVariantRow& operator*() noexcept
400 SqlVariantRow
const& operator*() const noexcept
405 SqlVariantRowIterator& operator++() noexcept
411 for (
auto const i: std::views::iota(SQLUSMALLINT(1), SQLUSMALLINT(_numResultColumns + 1)))
412 _row.emplace_back(_cursor->GetColumn<
SqlVariant>(i));
417 bool operator!=(SqlSentinelIterator )
const noexcept
422 bool operator!=(SqlVariantRowIterator
const& )
const noexcept
429 SQLUSMALLINT _numResultColumns = 0;
434class [[nodiscard]] SqlVariantRowCursor
438 _resultCursor { std::move(cursor) }
442 SqlVariantRowIterator begin() noexcept
444 return SqlVariantRowIterator { _resultCursor };
447 static SqlSentinelIterator end() noexcept
449 return SqlSentinelIterator {};
480 _connection { &conn }
487 using difference_type = bool;
488 using value_type = T;
490 iterator& operator++()
492 _is_end = !_stmt->FetchRow();
498 _stmt->CloseCursor();
503 LIGHTWEIGHT_FORCE_INLINE value_type operator*()
noexcept
507 Reflection::EnumerateMembers(res, [
this]<
size_t I>(
auto&& value) {
508 auto tmp = _stmt->GetColumn<
typename Reflection::MemberTypeOf<I, value_type>::ValueType>(I + 1);
515 LIGHTWEIGHT_FORCE_INLINE
constexpr bool operator!=(iterator
const& other)
const noexcept
517 return _is_end != other._is_end;
520 constexpr iterator(std::default_sentinel_t )
noexcept:
526 _stmt { std::make_unique<SqlStatement>(conn) }
530 LIGHTWEIGHT_FORCE_INLINE
SqlStatement& Statement()
noexcept
536 bool _is_end =
false;
537 std::unique_ptr<SqlStatement> _stmt;
542 auto it = iterator { *_connection };
543 auto& stmt = it.Statement();
544 stmt.
Prepare(it.Statement().Query(RecordTableName<T>).Select().template Fields<T>().All());
550 iterator end()
noexcept
552 return iterator { std::default_sentinel };
560inline LIGHTWEIGHT_FORCE_INLINE
bool SqlStatement::IsAlive() const noexcept
562 return m_connection && m_connection->
IsAlive() && m_hStmt !=
nullptr;
565inline LIGHTWEIGHT_FORCE_INLINE
bool SqlStatement::IsPrepared() const noexcept
567 return !m_preparedQuery.empty();
572 return *m_connection;
577 return *m_connection;
592 Prepare(queryObject.ToSql());
597 return Prepare(queryObject.ToSql());
600inline LIGHTWEIGHT_FORCE_INLINE std::string
const& SqlStatement::PreparedQuery() const noexcept
602 return m_preparedQuery;
605template <SqlOutputColumnBinder... Args>
611 ((++i, RequireSuccess(SqlDataBinder<Args>::OutputColumn(m_hStmt, i, args, GetIndicatorForColumn(i), *
this))), ...);
614template <
typename... Records>
615 requires(((std::is_class_v<Records> && std::is_aggregate_v<Records>) && ...))
621 ((Reflection::EnumerateMembers(*records,
622 [
this, &i]<
size_t I,
typename FieldType>(FieldType& value) {
624 RequireSuccess(SqlDataBinder<FieldType>::OutputColumn(
625 m_hStmt, i, &value, GetIndicatorForColumn(i), *
this));
630template <SqlOutputColumnBinder T>
631inline LIGHTWEIGHT_FORCE_INLINE
void SqlStatement::BindOutputColumn(SQLUSMALLINT columnIndex, T* arg)
635 RequireSuccess(SqlDataBinder<T>::OutputColumn(m_hStmt, columnIndex, arg, GetIndicatorForColumn(columnIndex), *
this));
638template <SqlInputParameterBinder Arg>
639inline LIGHTWEIGHT_FORCE_INLINE
void SqlStatement::BindInputParameter(SQLSMALLINT columnIndex, Arg
const& arg)
642 m_expectedParameterCount = (std::numeric_limits<
decltype(m_expectedParameterCount)>::max)();
643 RequireSuccess(SqlDataBinder<Arg>::InputParameter(m_hStmt, columnIndex, arg, *
this));
646template <SqlInputParameterBinder Arg,
typename ColumnName>
647inline LIGHTWEIGHT_FORCE_INLINE
void SqlStatement::BindInputParameter(SQLSMALLINT columnIndex,
649 ColumnName&& columnNameHint)
652 BindInputParameter(columnIndex, arg);
655template <SqlInputParameterBinder... Args>
664 if (!(m_expectedParameterCount == (std::numeric_limits<
decltype(m_expectedParameterCount)>::max)()
665 &&
sizeof...(args) == 0)
666 && !(m_expectedParameterCount ==
sizeof...(args)))
667 throw std::invalid_argument {
"Invalid argument count" };
672 RequireSuccess(SqlDataBinder<Args>::InputParameter(m_hStmt, i, args, *
this))),
675 auto const result = SQLExecute(m_hStmt);
677 if (result != SQL_NO_DATA && result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO)
680 ProcessPostExecuteCallbacks();
685concept SqlNativeContiguousValueConcept =
686 std::same_as<T, bool>
687 || std::same_as<T, char>
688 || std::same_as<T, unsigned char>
689 || std::same_as<T, wchar_t>
690 || std::same_as<T, std::int16_t>
691 || std::same_as<T, std::uint16_t>
692 || std::same_as<T, std::int32_t>
693 || std::same_as<T, std::uint32_t>
694 || std::same_as<T, std::int64_t>
695 || std::same_as<T, std::uint64_t>
696 || std::same_as<T, float>
697 || std::same_as<T, double>
698 || std::same_as<T, SqlDate>
699 || std::same_as<T, SqlTime>
700 || std::same_as<T, SqlDateTime>
701 || std::same_as<T, SqlFixedString<T::Capacity, typename T::value_type, T::PostRetrieveOperation>>;
703template <
typename FirstColumnBatch,
typename... MoreColumnBatches>
704concept SqlNativeBatchable =
705 std::ranges::contiguous_range<FirstColumnBatch>
706 && (std::ranges::contiguous_range<MoreColumnBatches> && ...)
707 && SqlNativeContiguousValueConcept<std::ranges::range_value_t<FirstColumnBatch>>
708 && (SqlNativeContiguousValueConcept<std::ranges::range_value_t<MoreColumnBatches>> && ...);
712template <SqlInputParameterBatchBinder FirstColumnBatch, std::ranges::contiguous_range... MoreColumnBatches>
714 MoreColumnBatches
const&... moreColumnBatches)
716 static_assert(SqlNativeBatchable<FirstColumnBatch, MoreColumnBatches...>,
717 "Must be a supported native contiguous element type.");
719 if (m_expectedParameterCount != 1 +
sizeof...(moreColumnBatches))
720 throw std::invalid_argument {
"Invalid number of columns" };
722 auto const rowCount = std::ranges::size(firstColumnBatch);
723 if (!((std::size(moreColumnBatches) == rowCount) && ...))
724 throw std::invalid_argument {
"Uneven number of rows" };
730 RequireSuccess(SQLSetStmtAttr(m_hStmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER) rowCount, 0));
731 RequireSuccess(SQLSetStmtAttr(m_hStmt, SQL_ATTR_PARAM_BIND_OFFSET_PTR, &rowStart, 0));
732 RequireSuccess(SQLSetStmtAttr(m_hStmt, SQL_ATTR_PARAM_BIND_TYPE, SQL_PARAM_BIND_BY_COLUMN, 0));
733 RequireSuccess(SQLSetStmtAttr(m_hStmt, SQL_ATTR_PARAM_OPERATION_PTR, SQL_PARAM_PROCEED, 0));
734 RequireSuccess(SqlDataBinder<std::remove_cvref_t<
decltype(*std::ranges::data(firstColumnBatch))>>::
735 InputParameter(m_hStmt, 1, *std::ranges::data(firstColumnBatch), *
this));
736 SQLUSMALLINT column = 1;
737 (RequireSuccess(SqlDataBinder<std::remove_cvref_t<
decltype(*std::ranges::data(moreColumnBatches))>>::
738 InputParameter(m_hStmt, ++column, *std::ranges::data(moreColumnBatches), *
this)), ...);
739 RequireSuccess(SQLExecute(m_hStmt));
740 ProcessPostExecuteCallbacks();
744template <SqlInputParameterBatchBinder FirstColumnBatch, std::ranges::range... MoreColumnBatches>
746 MoreColumnBatches
const&... moreColumnBatches)
750 if constexpr (SqlNativeBatchable<FirstColumnBatch, MoreColumnBatches...>)
756template <SqlInputParameterBatchBinder FirstColumnBatch, std::ranges::range... MoreColumnBatches>
759 if (m_expectedParameterCount != 1 +
sizeof...(moreColumnBatches))
760 throw std::invalid_argument {
"Invalid number of columns" };
762 auto const rowCount = std::ranges::size(firstColumnBatch);
763 if (!((std::size(moreColumnBatches) == rowCount) && ...))
764 throw std::invalid_argument {
"Uneven number of rows" };
766 for (
auto const rowIndex: std::views::iota(
size_t { 0 }, rowCount))
769 [&]<SqlInputParameterBinder... ColumnValues>(ColumnValues
const&... columnsInRow) {
770 SQLUSMALLINT column = 0;
771 ((++column, SqlDataBinder<ColumnValues>::InputParameter(m_hStmt, column, columnsInRow, *this)), ...);
772 RequireSuccess(SQLExecute(m_hStmt));
773 ProcessPostExecuteCallbacks();
775 std::make_tuple(std::ref(*std::ranges::next(std::ranges::begin(firstColumnBatch), rowIndex)),
776 std::ref(*std::ranges::next(std::ranges::begin(moreColumnBatches), rowIndex))...));
780template <SqlGetColumnNativeType T>
784 RequireSuccess(SqlDataBinder<T>::GetColumn(m_hStmt, column, result, &indicator, *
this));
785 return indicator != SQL_NULL_DATA;
792concept SqlNullableType = (std::same_as<T, SqlVariant> || IsSpecializationOf<std::optional, T>);
796template <SqlGetColumnNativeType T>
801 RequireSuccess(SqlDataBinder<T>::GetColumn(m_hStmt, column, &result, &indicator, *
this));
802 if constexpr (!detail::SqlNullableType<T>)
803 if (indicator == SQL_NULL_DATA)
804 throw std::runtime_error {
"Column value is NULL" };
808template <SqlGetColumnNativeType T>
813 RequireSuccess(SqlDataBinder<T>::GetColumn(m_hStmt, column, &result, &indicator, *
this));
814 if (indicator == SQL_NULL_DATA)
816 return { std::move(result) };
820 std::source_location location)
825template <
typename Callable>
826 requires std::invocable<Callable, SqlMigrationQueryBuilder&>
831 auto const queries = migration.GetPlan().ToSql();
832 for (
auto const& query: queries)
840 requires(!std::same_as<T, SqlVariant>)
843 auto const _ = detail::Finally([
this] { CloseCursor(); });
844 ExecuteDirect(query, location);
845 RequireSuccess(FetchRow());
846 return GetNullableColumn<T>(1);
850 requires(std::same_as<T, SqlVariant>)
853 auto const _ = detail::Finally([
this] { CloseCursor(); });
854 ExecuteDirect(query, location);
855 RequireSuccess(FetchRow());
856 if (
auto result = GetNullableColumn<T>(1); result.has_value())
862 requires(!std::same_as<T, SqlVariant>)
865 return ExecuteDirectScalar<T>(query.ToSql(), location);
869 requires(std::same_as<T, SqlVariant>)
872 return ExecuteDirectScalar<T>(query.ToSql(), location);
878 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.
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.
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.
LIGHTWEIGHT_API SqlStatement(std::nullopt_t)
Construct a new empty SqlStatement object. No SqlConnection is associated with this statement.
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(SqlConnection &relatedConnection)
Construct a new SqlStatement object, using the given connection.
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.