Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions dev/ast/labeled_bindable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#pragma once
#ifndef SQLITE_ORM_IMPORT_STD_MODULE
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
#include <type_traits> // std::remove_const
#include <utility>
#endif
#endif

#include "../functional/cxx_type_traits_polyfill.h"
#include "../functional/cstring_literal.h"
#include "../statement_binding_traits.h"

namespace sqlite_orm::internal {
template<class T>
using access_bindable_t = polyfill::detected_or_t<T, type_t, T>;
}

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
namespace sqlite_orm::internal {
template<class Moniker, class T>
struct labeled_bindable {
using name_constant_type = Moniker;
using type = T;

type value;
};

template<class Moniker>
struct bindable_label : Moniker {
using name_constant_type = Moniker;
};

// note: not in use because AST iteration walks through to the value leaf
template<class Moniker, class T>
T& access_bindable(const labeled_bindable<Moniker, T>& t) = delete;

template<class T>
using name_constant_type_t = typename T::name_constant_type;

template<class T>
using name_constant_type_or_none_t = polyfill::detected_t<name_constant_type_t, T>;
}

SQLITE_ORM_EXPORT namespace sqlite_orm {
// Intentionally place operators for types classified as arithmetic or general operator arguments in the internal namespace
// to facilitate ADL (Argument Dependent Lookup)
namespace internal {
/*
Associates a bindable value with a moniker.
*/
template<class T, class Moniker>
requires is_bindable_v<T>
constexpr labeled_bindable<Moniker, T> operator>>=(T bindable, const bindable_label<Moniker>&) {
return {std::move(bindable)};
}

/*
Associates a referenced bindable value with a moniker.
*/
template<class T, class Moniker>
requires is_bindable_v<T>
constexpr labeled_bindable<Moniker, T> operator>>=(std::reference_wrapper<T> bindable,
const bindable_label<Moniker>&) {
return {bindable};
}
}

inline namespace literals {
/*
* Make a label for a bindable from a string literal.
* E.g. "myparam"_bindable
*/
template<internal::cstring_literal name>
[[nodiscard]] consteval auto operator"" _bindable() {
using name_constant_type = std::integral_constant<decltype(name), name>;
return internal::bindable_label<name_constant_type>{};
}
}

/** @short Specifies that a type is an integral constant C-string usable as a label for a bindable.
*/
template<class T>
concept orm_bindable_label = polyfill::is_specialization_of_v<std::remove_const_t<T>, internal::bindable_label>;
}
#endif
89 changes: 89 additions & 0 deletions dev/ast/named_parameter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#pragma once
#pragma once
#ifndef SQLITE_ORM_IMPORT_STD_MODULE
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
#include <type_traits> // std::remove_const
#include <algorithm>
#include <utility>
#include <memory>
#endif
#endif

#include "../functional/cxx_type_traits_polyfill.h"
#include "../functional/cstring_literal.h"
#include "../statement_binding_traits.h"

namespace sqlite_orm::internal {
template<class T>
using access_bindable_t = polyfill::detected_or_t<T, type_t, T>;

template<class T>
T& access_bindable(T& t) {
return t;
}
}

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
namespace sqlite_orm::internal {
/*
* @note Named SQL parameters can be used multiple times in an SQL statement, which is the reason that we use shared_ptr here.
*/
template<class Moniker, class T>
struct named_bindable {
using name_constant_type = Moniker;
using type = T;

std::shared_ptr<type> value;

template<class U = type>
void operator=(U&& other) {
*value = std::forward<U>(other);
}
};

template<class Moniker>
struct parameter_moniker : Moniker {
using name_constant_type = Moniker;

/*
* Create a named SQL parameter argument of type T.
*/
template<class T, class... Args>
[[nodiscard]] constexpr named_bindable<Moniker, T> create(Args&&... args) const {
return {std::make_shared<T>(std::forward<Args>(args)...)};
}
};

template<class Moniker, class T>
T& access_bindable(const named_bindable<Moniker, T>& bindable) {
return *bindable.value;
}

template<class T>
using name_constant_type_t = typename T::name_constant_type;

template<class T>
using name_constant_type_or_none_t = polyfill::detected_t<name_constant_type_t, T>;
}

SQLITE_ORM_EXPORT namespace sqlite_orm {
inline namespace literals {
/*
* Make a moniker for a named parameter from a string literal.
* E.g. "myparam"_param
*/
template<internal::cstring_literal moniker>
[[nodiscard]] consteval auto operator"" _param() {
static_assert(moniker.cstr[0] == ':' || moniker.cstr[0] == '@');
using name_constant_type = std::integral_constant<decltype(moniker), moniker>;
return internal::parameter_moniker<name_constant_type>{};
}
}

/** @short Specifies that a type is an integral constant C-string usable for a named parameter.
*/
template<class T>
concept orm_parameter_moniker =
polyfill::is_specialization_of_v<std::remove_const_t<T>, internal::parameter_moniker>;
}
#endif
13 changes: 13 additions & 0 deletions dev/ast_iterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "ast/match.h"
#include "ast/cast.h"
#include "ast/limit.h"
#include "ast/labeled_bindable.h"

namespace sqlite_orm {

Expand Down Expand Up @@ -514,6 +515,18 @@ namespace sqlite_orm {
}
};

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
template<class T>
struct ast_iterator<T, match_specialization_of<T, labeled_bindable>> {
using node_type = T;

template<class L>
SQLITE_ORM_STATIC_CALLOP void operator()(const node_type& param, L& lambda) SQLITE_ORM_OR_CONST_CALLOP {
iterate_ast(param.value, lambda);
}
};
#endif

template<class F, class... CallArgs>
struct ast_iterator<function_call<F, CallArgs...>, void> {
using node_type = function_call<F, CallArgs...>;
Expand Down
19 changes: 19 additions & 0 deletions dev/column_result.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#include "cte_types.h"
#include "storage_traits.h"
#include "function.h"
#include "ast/labeled_bindable.h"
#include "ast/named_parameter.h"
#include "ast/special_keywords.h"
#include "ast/cast.h"

Expand Down Expand Up @@ -127,6 +129,23 @@ namespace sqlite_orm {
using type = std::unique_ptr<column_result_of_t<DBOs, X>>;
};

template<class DBOs, class P, class T, class D>
struct column_result_t<DBOs, pointer_binding<P, T, D>, void> {
using type = std::nullptr_t;
};

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
template<class DBOs, class T>
struct column_result_t<DBOs, T, match_specialization_of<T, labeled_bindable>> {
using type = typename T::type;
};

template<class DBOs, class T>
struct column_result_t<DBOs, T, match_specialization_of<T, named_bindable>> {
using type = typename T::type;
};
#endif

template<class DBOs, class T>
struct column_result_t<DBOs, count_asterisk_t<T>, void> {
using type = int;
Expand Down
103 changes: 85 additions & 18 deletions dev/get_prepared_statement.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#endif

#include "functional/cxx_type_traits_polyfill.h"
#include "ast/labeled_bindable.h"
#include "ast/named_parameter.h"
#include "type_traits.h"
#include "prepared_statement.h"
#include "ast_iterator.h"
Expand Down Expand Up @@ -126,19 +128,22 @@ SQLITE_ORM_EXPORT namespace sqlite_orm {

template<int N, class T>
const auto& get(const internal::prepared_statement_t<T>& statement) {
using namespace ::sqlite_orm::internal;
using statement_type = polyfill::remove_cvref_t<decltype(statement)>;
using expression_type = internal::expression_type_t<statement_type>;
using node_tuple = internal::node_tuple_t<expression_type>;
using bind_tuple = internal::bindable_filter_t<node_tuple>;
using result_type = std::tuple_element_t<static_cast<size_t>(N), bind_tuple>;
using expression_type = expression_type_t<statement_type>;
using node_tuple = node_tuple_t<expression_type>;
using bind_tuple = bindable_filter_t<node_tuple>;
using bound_type = std::tuple_element_t<static_cast<size_t>(N), bind_tuple>;
using result_type = access_bindable_t<bound_type>;

const result_type* result = nullptr;
internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable {
using node_type = polyfill::remove_cvref_t<decltype(node)>;
if constexpr (internal::is_bindable<node_type>::value) {
iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable {
using leaf_type = polyfill::remove_cvref_t<decltype(node)>;
if constexpr (is_sql_parameter<leaf_type>::value) {
++index;
if constexpr (std::is_same<result_type, node_type>::value) {
if constexpr (std::is_same<result_type, access_bindable_t<leaf_type>>::value) {
if (index == N) {
result = &node;
result = &access_bindable(node);
}
}
}
Expand All @@ -148,24 +153,86 @@ SQLITE_ORM_EXPORT namespace sqlite_orm {

template<int N, class T>
auto& get(internal::prepared_statement_t<T>& statement) {
using namespace ::sqlite_orm::internal;
using statement_type = std::remove_reference_t<decltype(statement)>;
using expression_type = internal::expression_type_t<statement_type>;
using node_tuple = internal::node_tuple_t<expression_type>;
using bind_tuple = internal::bindable_filter_t<node_tuple>;
using result_type = std::tuple_element_t<static_cast<size_t>(N), bind_tuple>;
using expression_type = expression_type_t<statement_type>;
using node_tuple = node_tuple_t<expression_type>;
using bind_tuple = bindable_filter_t<node_tuple>;
using bound_type = std::tuple_element_t<static_cast<size_t>(N), bind_tuple>;
using result_type = access_bindable_t<bound_type>;

result_type* result = nullptr;
iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable {
using leaf_type = polyfill::remove_cvref_t<decltype(node)>;
if constexpr (is_sql_parameter<leaf_type>::value) {
++index;
if constexpr (std::is_same<result_type, access_bindable_t<leaf_type>>::value) {
if (index == N) {
result = const_cast<result_type*>(&access_bindable(node));
}
}
}
});
return internal::get_ref(*result);
}

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
template<auto name, class T>
requires (orm_parameter_moniker<decltype(name)> || orm_bindable_label<decltype(name)>)
const auto& access(const internal::prepared_statement_t<T>& statement) {
using namespace ::sqlite_orm::internal;
using statement_type = std::remove_cvref_t<decltype(statement)>;
using expression_type = expression_type_t<statement_type>;
using node_tuple = node_tuple_t<expression_type>;
using bind_tuple = bindable_filter_t<node_tuple>;
using index_type =
find_tuple_type<bind_tuple, name_constant_type_t<decltype(name)>, name_constant_type_or_none_t>;
constexpr size_t N = index_type::value;
static_assert(N < std::tuple_size_v<bind_tuple>, "No such named bindable found in prepared statement");
using bound_type = std::tuple_element_t<N, bind_tuple>;
using result_type = access_bindable_t<bound_type>;

const result_type* result = nullptr;
iterate_ast(statement.expression, [&result, index = -1]<class leaf_type>(const leaf_type& node) mutable {
if constexpr (is_sql_parameter<leaf_type>::value) {
++index;
if constexpr (std::is_same_v<result_type, access_bindable_t<leaf_type>>) {
if (index == N) {
result = &access_bindable(node);
}
}
}
});
return internal::get_ref(*result);
}

internal::iterate_ast(statement.expression, [&result, index = -1](auto& node) mutable {
using node_type = polyfill::remove_cvref_t<decltype(node)>;
if constexpr (internal::is_bindable<node_type>::value) {
template<auto name, class T>
requires (orm_parameter_moniker<decltype(name)> || orm_bindable_label<decltype(name)>)
auto& access(internal::prepared_statement_t<T>& statement) {
using namespace ::sqlite_orm::internal;
using statement_type = std::remove_cvref_t<decltype(statement)>;
using expression_type = expression_type_t<statement_type>;
using node_tuple = node_tuple_t<expression_type>;
using bind_tuple = bindable_filter_t<node_tuple>;
using index_type =
find_tuple_type<bind_tuple, name_constant_type_t<decltype(name)>, name_constant_type_or_none_t>;
constexpr size_t N = index_type::value;
static_assert(N < std::tuple_size_v<bind_tuple>, "No such named bindable found in prepared statement");
using bound_type = std::tuple_element_t<N, bind_tuple>;
using result_type = access_bindable_t<bound_type>;

result_type* result = nullptr;
iterate_ast(statement.expression, [&result, index = -1]<class leaf_type>(const leaf_type& node) mutable {
if constexpr (is_sql_parameter<leaf_type>::value) {
++index;
if constexpr (std::is_same<result_type, node_type>::value) {
if constexpr (std::is_same_v<result_type, access_bindable_t<leaf_type>>) {
if (index == N) {
result = const_cast<result_type*>(&node);
result = const_cast<result_type*>(&access_bindable(node));
}
}
}
});
return internal::get_ref(*result);
}
#endif
}
6 changes: 0 additions & 6 deletions dev/node_tuple.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,6 @@ namespace sqlite_orm {
template<char... C>
struct node_tuple<column_alias<C...>, void> : node_tuple<void> {};

/**
* Literal
*/
template<class T>
struct node_tuple<literal_holder<T>, void> : node_tuple<void> {};

template<class E>
struct node_tuple<order_by_t<E>, void> : node_tuple<E> {};

Expand Down
Loading