Files
TinyORM/include/orm/query/querybuilder.hpp
silverqx bb1fbbc991 fixtypo
[skip ci]
2023-11-06 20:13:28 +01:00

1801 lines
72 KiB
C++

#pragma once
#ifndef ORM_QUERY_QUERYBUILDER_HPP
#define ORM_QUERY_QUERYBUILDER_HPP
#include "orm/macros/systemheader.hpp"
TINY_SYSTEM_HEADER
#include "orm/query/concerns/buildsqueries.hpp"
#include "orm/query/grammars/grammar.hpp"
#include "orm/utils/query.hpp"
TINYORM_BEGIN_COMMON_NAMESPACE
#ifndef TINYORM_DISABLE_ORM
namespace Orm::Tiny
{
template<typename Model>
class Builder;
}
#endif
namespace Orm::Query
{
class JoinClause;
/*! Concept for the remove() method parameter. */
template<typename T>
concept Remove = std::convertible_to<T, quint64> ||
std::convertible_to<T, Query::Expression>;
// TODO querybuilder, whereFullText, whereBitwise silverqx
// FUTURE querybuilder, paginator silverqx
// FUTURE querybuilder, index hint silverqx
/*! Database query builder. */
class SHAREDLIB_EXPORT Builder : public Concerns::BuildsQueries // clazy:exclude=copyable-polymorphic
{
// To access enforceOrderBy()
friend Concerns::BuildsQueries;
#ifndef TINYORM_DISABLE_ORM
// To access stripTableForPluck()
template<typename Model>
friend class Tiny::Builder;
#endif
/*! Alias for the query grammar. */
using QueryGrammar = Query::Grammars::Grammar;
/*! Alias for query utils. */
using QueryUtils = Orm::Utils::Query;
public:
/*! Constructor. */
Builder(std::shared_ptr<DatabaseConnection> connection,
std::shared_ptr<QueryGrammar> grammar);
/* Need to be the polymorphic type because of dynamic_cast<>
in the Grammar::concatenateWhereClauses(). */
/*! Virtual destructor. */
inline ~Builder() override = default;
/*! Copy constructor. */
inline Builder(const Builder &) = default;
/*! Deleted copy assignment operator (class contains reference and const). */
Builder &operator=(const Builder &) = delete;
/*! Move constructor. */
inline Builder(Builder &&) noexcept = default;
/*! Deleted move assignment operator (class contains reference and const). */
Builder &operator=(Builder &&) = delete;
/* Retrieving results */
/*! Execute the query as a "select" statement. */
SqlQuery get(const QVector<Column> &columns = {ASTERISK});
/*! Execute a query for a single record by ID. */
SqlQuery find(const QVariant &id, const QVector<Column> &columns = {ASTERISK});
/*! Execute a query for a single record by ID or call a callback. */
SqlQuery findOr(const QVariant &id, const QVector<Column> &columns,
const std::function<void()> &callback);
/*! Execute a query for a single record by ID or call a callback. */
inline SqlQuery findOr(const QVariant &id,
const std::function<void()> &callback);
/*! Execute a query for a single record by ID or call a callback. */
template<typename R>
std::pair<SqlQuery, R>
findOr(const QVariant &id, const QVector<Column> &columns,
const std::function<R()> &callback);
/*! Execute a query for a single record by ID or call a callback. */
template<typename R>
std::pair<SqlQuery, R>
findOr(const QVariant &id, const std::function<R()> &callback);
/*! Execute the query and get the first result. */
SqlQuery first(const QVector<Column> &columns = {ASTERISK});
/*! Get a single column's value from the first result of a query. */
QVariant value(const Column &column);
/*! Get a single column's value from the first result of a query if it's
the sole matching record. */
QVariant soleValue(const Column &column);
/*! Get a vector with values in the given column. */
QVector<QVariant> pluck(const Column &column);
/*! Get a map with values in the given column and keyed by values in the key
column. */
template<typename T>
std::map<T, QVariant> pluck(const Column &column, const Column &key);
/*! Concatenate values of the given column as a string. */
QString implode(const QString &column, const QString &glue = "");
/*! Get the SQL representation of the query. */
QString toSql();
/* Insert, Update, Delete */
/*! Insert new records into the database (multi-rows insert). */
std::optional<SqlQuery>
insert(const QVector<QVariantMap> &values);
/*! Insert a new record into the database. */
std::optional<SqlQuery>
insert(const QVariantMap &values);
/*! Insert new records into the database (multi insert with separated columns). */
std::optional<SqlQuery>
insert(const QVector<QString> &columns, const QVector<QVector<QVariant>> &values);
/*! Insert a new record and get the value of the primary key. */
quint64 insertGetId(const QVariantMap &values, const QString &sequence = "");
/*! Insert new records into the database while ignoring errors. */
std::tuple<int, std::optional<QSqlQuery>>
insertOrIgnore(const QVector<QVariantMap> &values);
/*! Insert a new record into the database while ignoring errors. */
std::tuple<int, std::optional<QSqlQuery>>
insertOrIgnore(const QVariantMap &values);
/*! Insert new records into the database while ignoring errors (multi insert). */
std::tuple<int, std::optional<QSqlQuery>>
insertOrIgnore(const QVector<QString> &columns,
const QVector<QVector<QVariant>> &values);
/*! Update records in the database. */
std::tuple<int, QSqlQuery>
update(const QVector<UpdateItem> &values);
/*! Insert or update a record matching the attributes, and fill it with values. */
std::tuple<int, std::optional<QSqlQuery>>
updateOrInsert(const QVector<WhereItem> &attributes,
const QVector<UpdateItem> &values);
/*! Insert new records or update the existing ones. */
std::tuple<int, std::optional<QSqlQuery>>
upsert(const QVector<QVariantMap> &values, const QStringList &uniqueBy,
const QStringList &update);
/*! Insert new records or update the existing ones (update all columns). */
std::tuple<int, std::optional<QSqlQuery>>
upsert(const QVector<QVariantMap> &values, const QStringList &uniqueBy);
/*! Delete records from the database. */
std::tuple<int, QSqlQuery> deleteRow();
/*! Delete records from the database. */
std::tuple<int, QSqlQuery> remove();
/*! Delete records from the database. */
template<Remove T>
inline std::tuple<int, QSqlQuery> deleteRow(T &&id);
/*! Delete records from the database. */
template<Remove T>
std::tuple<int, QSqlQuery> remove(T &&id);
/*! Run a truncate statement on the table. */
void truncate();
/* Select */
/*! Retrieve the "count" result of the query. */
inline quint64 count(const QVector<Column> &columns = {ASTERISK}) const;
/*! Retrieve the "count" result of the query. */
template<typename = void>
inline quint64 count(const Column &column);
/*! Retrieve the minimum value of a given column. */
inline QVariant min(const Column &column) const;
/*! Retrieve the maximum value of a given column. */
inline QVariant max(const Column &column) const;
/*! Retrieve the sum of the values of a given column. */
inline QVariant sum(const Column &column) const;
/*! Retrieve the average of the values of a given column. */
inline QVariant avg(const Column &column) const;
/*! Alias for the "avg" method. */
inline QVariant average(const Column &column) const;
/*! Execute an aggregate function on the database. */
QVariant aggregate(const QString &function,
const QVector<Column> &columns = {ASTERISK}) const;
/*! Determine if any rows exist for the current query. */
bool exists();
/*! Determine if no rows exist for the current query. */
inline bool doesntExist();
/*! Execute the given callback if no rows exist for the current query. */
bool existsOr(const std::function<void()> &callback);
/*! Execute the given callback if rows exist for the current query. */
bool doesntExistOr(const std::function<void()> &callback);
/*! Execute the given callback if no rows exist for the current query. */
template<typename R>
std::pair<bool, R> existsOr(const std::function<R()> &callback);
template<typename R>
/*! Execute the given callback if rows exist for the current query. */
std::pair<bool, R> doesntExistOr(const std::function<R()> &callback);
/*! Set the columns to be selected. */
Builder &select(const QVector<Column> &columns = {ASTERISK});
/*! Set the column to be selected. */
Builder &select(const Column &column);
/*! Add new select columns to the query. */
Builder &addSelect(const QVector<Column> &columns);
/*! Add a new select column to the query. */
Builder &addSelect(const Column &column);
/*! Set the columns to be selected. */
Builder &select(QVector<Column> &&columns);
/*! Set the column to be selected. */
Builder &select(Column &&column);
/*! Add new select columns to the query. */
Builder &addSelect(QVector<Column> &&columns);
/*! Add a new select column to the query. */
Builder &addSelect(Column &&column);
/*! Set a select subquery on the query. */
template<Queryable T>
inline Builder &select(T &&query, const QString &as);
/*! Add a select subquery to the query. */
template<Queryable T>
Builder &addSelect(T &&query, const QString &as);
/*! Add a subselect expression to the query. */
template<SubQuery T>
Builder &selectSub(T &&query, const QString &as);
/*! Add a new "raw" select expression to the query. */
Builder &selectRaw(const QString &expression,
const QVector<QVariant> &bindings = {});
/*! Force the query to only return distinct results. */
Builder &distinct();
/*! Force the query to only return distinct results. */
Builder &distinct(const QStringList &columns);
/*! Force the query to only return distinct results. */
Builder &distinct(QStringList &&columns);
/*! Set the table which the query is targeting. */
Builder &from(const QString &table, const QString &as = "");
/*! Set the table which the query is targeting. */
Builder &from(const Expression &table);
/*! Set the table which the query is targeting. */
Builder &from(Expression &&table);
/*! Makes "from" fetch from a subquery. */
template<SubQuery T>
Builder &fromSub(T &&query, const QString &as);
/*! Set the table which the query is targeting. */
Builder &fromRaw(const QString &expression,
const QVector<QVariant> &bindings = {});
/* Joins */
/*! Add a join clause to the query. */
template<JoinTable T>
inline Builder &
join(T &&table, const QString &first, const QString &comparison,
const QVariant &second, const QString &type = INNER, bool where = false);
/*! Add an advanced join clause to the query. */
template<JoinTable T>
inline Builder &
join(T &&table, const std::function<void(JoinClause &)> &callback,
const QString &type = INNER);
/*! Add a "join where" clause to the query. */
template<JoinTable T>
inline Builder &
joinWhere(T &&table, const QString &first, const QString &comparison,
const QVariant &second, const QString &type = INNER);
/*! Add a left join to the query. */
template<JoinTable T>
inline Builder &
leftJoin(T &&table, const QString &first, const QString &comparison,
const QVariant &second);
/*! Add an advanced left join to the query. */
template<JoinTable T>
inline Builder &
leftJoin(T &&table, const std::function<void(JoinClause &)> &callback);
/*! Add a "join where" clause to the query. */
template<JoinTable T>
inline Builder &
leftJoinWhere(T &&table, const QString &first, const QString &comparison,
const QVariant &second);
/*! Add a right join to the query. */
template<JoinTable T>
inline Builder &
rightJoin(T &&table, const QString &first, const QString &comparison,
const QVariant &second);
/*! Add an advanced right join to the query. */
template<JoinTable T>
inline Builder &
rightJoin(T &&table, const std::function<void(JoinClause &)> &callback);
/*! Add a "right join where" clause to the query. */
template<JoinTable T>
inline Builder &
rightJoinWhere(T &&table, const QString &first, const QString &comparison,
const QVariant &second);
/*! Add a "cross join" clause to the query. */
template<JoinTable T>
inline Builder &
crossJoin(T &&table, const QString &first, const QString &comparison,
const QVariant &second);
/*! Add an advanced "cross join" clause to the query. */
template<JoinTable T>
inline Builder &
crossJoin(T &&table, const std::function<void(JoinClause &)> &callback);
/*! Add a "cross join" clause to the query. */
template<JoinTable T>
inline Builder &crossJoin(T &&table);
/*! Add a subquery join clause to the query. */
template<SubQuery T>
inline Builder &
joinSub(T &&query, const QString &as, const QString &first,
const QString &comparison, const QVariant &second,
const QString &type = INNER, bool where = false);
/*! Add a subquery join clause to the query. */
template<SubQuery T>
inline Builder &
joinSub(T &&query, const QString &as,
const std::function<void(JoinClause &)> &callback,
const QString &type = INNER);
/*! Add a subquery left join to the query. */
template<SubQuery T>
inline Builder &
leftJoinSub(T &&query, const QString &as, const QString &first,
const QString &comparison, const QVariant &second);
/*! Add a subquery left join to the query. */
template<SubQuery T>
inline Builder &
leftJoinSub(T &&query, const QString &as,
const std::function<void(JoinClause &)> &callback);
/*! Add a subquery right join to the query. */
template<SubQuery T>
inline Builder &
rightJoinSub(T &&query, const QString &as, const QString &first,
const QString &comparison, const QVariant &second);
/*! Add a subquery right join to the query. */
template<SubQuery T>
inline Builder &
rightJoinSub(T &&query, const QString &as,
const std::function<void(JoinClause &)> &callback);
/* General where */
/*! Add a basic where clause to the query. */
template<WhereValue T>
Builder &where(const Column &column, const QString &comparison,
T &&value, const QString &condition = AND);
/*! Add an "or where" clause to the query. */
template<WhereValue T>
Builder &orWhere(const Column &column, const QString &comparison, T &&value);
/*! Add a basic equal where clause to the query. */
template<WhereValue T>
Builder &whereEq(const Column &column, T &&value,
const QString &condition = AND);
/*! Add an equal "or where" clause to the query. */
template<WhereValue T>
Builder &orWhereEq(const Column &column, T &&value);
/* General where not */
/*! Add a basic "where not" clause to the query. */
template<WhereValue T>
Builder &whereNot(const Column &column, const QString &comparison,
T &&value, const QString &condition = AND);
/*! Add an "or where not" clause to the query. */
template<WhereValue T>
Builder &orWhereNot(const Column &column, const QString &comparison, T &&value);
/*! Add a basic equal "where not" clause to the query. */
template<WhereValue T>
Builder &whereNotEq(const Column &column, T &&value,
const QString &condition = AND);
/*! Add an equal "or where not" clause to the query. */
template<WhereValue T>
Builder &orWhereNotEq(const Column &column, T &&value);
/* Nested where */
/*! Add a nested where clause to the query. */
Builder &where(const std::function<void(Builder &)> &callback,
const QString &condition = AND);
/*! Add a nested "or where" clause to the query. */
Builder &orWhere(const std::function<void(Builder &)> &callback);
/*! Add a nested "where not" clause to the query. */
Builder &whereNot(const std::function<void(Builder &)> &callback,
const QString &condition = AND);
/*! Add a nested "or where not" clause to the query. */
Builder &orWhereNot(const std::function<void(Builder &)> &callback);
/* Array where */
/*! Add a vector of basic where clauses to the query. */
Builder &where(const QVector<WhereItem> &values,
const QString &condition = AND,
const QString &defaultCondition = "");
/*! Add a vector of basic "or where" clauses to the query. */
Builder &orWhere(const QVector<WhereItem> &values,
const QString &defaultCondition = "");
/*! Add a vector of basic "where not" clauses to the query. */
Builder &whereNot(const QVector<WhereItem> &values,
const QString &condition = AND,
const QString &defaultCondition = "");
/*! Add a vector of basic "or where not" clauses to the query. */
Builder &orWhereNot(const QVector<WhereItem> &values,
const QString &defaultCondition = "");
/* where column */
/*! Add a vector of where clauses comparing two columns to the query. */
Builder &whereColumn(const QVector<WhereColumnItem> &values,
const QString &condition = AND);
/*! Add a vector of "or where" clauses comparing two columns to the query. */
Builder &orWhereColumn(const QVector<WhereColumnItem> &values);
/*! Add a "where" clause comparing two columns to the query. */
Builder &whereColumn(const Column &first, const QString &comparison,
const Column &second, const QString &condition = AND);
/*! Add a "or where" clause comparing two columns to the query. */
Builder &orWhereColumn(const Column &first, const QString &comparison,
const Column &second);
/*! Add an equal "where" clause comparing two columns to the query. */
Builder &whereColumnEq(const Column &first, const Column &second,
const QString &condition = AND);
/*! Add an equal "or where" clause comparing two columns to the query. */
Builder &orWhereColumnEq(const Column &first, const Column &second);
/* where IN */
/*! Add a "where in" clause to the query. */
Builder &whereIn(const Column &column, const QVector<QVariant> &values,
const QString &condition = AND, bool nope = false);
/*! Add an "or where in" clause to the query. */
Builder &orWhereIn(const Column &column, const QVector<QVariant> &values);
/*! Add a "where not in" clause to the query. */
Builder &whereNotIn(const Column &column, const QVector<QVariant> &values,
const QString &condition = AND);
/*! Add an "or where not in" clause to the query. */
Builder &orWhereNotIn(const Column &column, const QVector<QVariant> &values);
/* where null */
/*! Add a "where null" clause to the query. */
Builder &whereNull(const QVector<Column> &columns = {ASTERISK},
const QString &condition = AND, bool nope = false);
/*! Add an "or where null" clause to the query. */
Builder &orWhereNull(const QVector<Column> &columns = {ASTERISK});
/*! Add a "where not null" clause to the query. */
Builder &whereNotNull(const QVector<Column> &columns = {ASTERISK},
const QString &condition = AND);
/*! Add an "or where not null" clause to the query. */
Builder &orWhereNotNull(const QVector<Column> &columns = {ASTERISK});
/*! Add a "where null" clause to the query. */
Builder &whereNull(const Column &column, const QString &condition = AND,
bool nope = false);
/*! Add an "or where null" clause to the query. */
Builder &orWhereNull(const Column &column);
/*! Add a "where not null" clause to the query. */
Builder &whereNotNull(const Column &column, const QString &condition = AND);
/*! Add an "or where not null" clause to the query. */
Builder &orWhereNotNull(const Column &column);
/* where between */
/*! Add a "where between" statement to the query. */
Builder &whereBetween(const Column &column, const WhereBetweenItem &values,
const QString &condition = AND, bool nope = false);
/*! Add an "or where between" statement to the query. */
Builder &orWhereBetween(const Column &column, const WhereBetweenItem &values);
/*! Add a "where not between" statement to the query. */
Builder &whereNotBetween(const Column &column, const WhereBetweenItem &values,
const QString &condition = AND);
/*! Add an "or where not between" statement to the query. */
Builder &orWhereNotBetween(const Column &column, const WhereBetweenItem &values);
/* where between columns */
/*! Add a "where between" statement using columns to the query. */
Builder &whereBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns,
const QString &condition = AND, bool nope = false);
/*! Add an "or where between" statement using columns to the query. */
Builder &orWhereBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns);
/*! Add a "where not between" statement using columns to the query. */
Builder &whereNotBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns,
const QString &condition = AND);
/*! Add an "or where not between" statement using columns to the query. */
Builder &orWhereNotBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns);
/* where sub-queries */
/*! Add a basic where clause to the query with a full sub-select column. */
template<Queryable C, WhereValue V>
Builder &where(C &&column, const QString &comparison, V &&value,
const QString &condition = AND);
/*! Add an "or where" clause to the query with a full sub-select column. */
template<Queryable C, WhereValue V>
inline Builder &orWhere(C &&column, const QString &comparison, V &&value);
/*! Add a basic equal where clause to the query with a full sub-select column. */
template<Queryable C, WhereValue V>
inline Builder &whereEq(C &&column, V &&value, const QString &condition = AND);
/*! Add an equal "or where" clause to the query with a full sub-select column. */
template<Queryable C, WhereValue V>
inline Builder &orWhereEq(C &&column, V &&value);
/* where not sub-queries */
/*! Add a basic "where not" clause to the query with a full sub-select column. */
template<Queryable C, WhereValue V>
Builder &whereNot(C &&column, const QString &comparison, V &&value,
const QString &condition = AND);
/*! Add an "or where not" clause to the query with a full sub-select column. */
template<Queryable C, WhereValue V>
inline Builder &orWhereNot(C &&column, const QString &comparison, V &&value);
/*! Add a basic equal "where not" clause to the query with a full sub-select
column. */
template<Queryable C, WhereValue V>
inline Builder &whereNotEq(C &&column, V &&value, const QString &condition = AND);
/*! Add an equal "or where not" clause to the query with a full sub-select
column. */
template<Queryable C, WhereValue V>
inline Builder &orWhereNotEq(C &&column, V &&value);
/*! Add a full sub-select to the "where" clause. */
template<WhereValueSubQuery T>
Builder &whereSub(const Column &column, const QString &comparison, T &&query,
const QString &condition = AND);
/* where exists */
/*! Add an exists clause to the query. */
template<QueryableShared C>
Builder &whereExists(C &&callback, const QString &condition = AND,
bool nope = false);
/*! Add an or exists clause to the query. */
template<QueryableShared C>
Builder &orWhereExists(C &&callback, bool nope = false);
/*! Add a where not exists clause to the query. */
template<QueryableShared C>
Builder &whereNotExists(C &&callback, const QString &condition = AND);
/*! Add a where not exists clause to the query. */
template<QueryableShared C>
Builder &orWhereNotExists(C &&callback);
/* where row values */
/*! Adds a where condition using row values. */
Builder &
whereRowValues(const QVector<Column> &columns, const QString &comparison,
const QVector<QVariant> &values, const QString &condition = AND);
/*! Adds an or where condition using row values. */
Builder &
orWhereRowValues(const QVector<Column> &columns, const QString &comparison,
const QVector<QVariant> &values);
Builder &
whereRowValuesEq(const QVector<Column> &columns, const QVector<QVariant> &values,
const QString &condition = AND);
/*! Adds an or where condition using row values. */
Builder &
orWhereRowValuesEq(const QVector<Column> &columns,
const QVector<QVariant> &values);
/* where dates */
/*! Add a "where date" statement to the query. */
Builder &whereDate(const Column &column, const QString &comparison,
QVariant value, const QString &condition = AND);
/*! Add a "where time" statement to the query. */
Builder &whereTime(const Column &column, const QString &comparison,
QVariant value, const QString &condition = AND);
/*! Add a "where day" statement to the query. */
Builder &whereDay(const Column &column, const QString &comparison,
QVariant value, const QString &condition = AND);
/*! Add a "where month" statement to the query. */
Builder &whereMonth(const Column &column, const QString &comparison,
QVariant value, const QString &condition = AND);
/*! Add a "where year" statement to the query. */
Builder &whereYear(const Column &column, const QString &comparison,
QVariant value, const QString &condition = AND);
/*! Add an equal "where date" statement to the query. */
inline Builder &whereEqDate(const Column &column, QVariant value,
const QString &condition = AND);
/*! Add an equal "where time" statement to the query. */
inline Builder &whereEqTime(const Column &column, QVariant value,
const QString &condition = AND);
/*! Add an equal "where day" statement to the query. */
inline Builder &whereEqDay(const Column &column, QVariant value,
const QString &condition = AND);
/*! Add an equal "where month" statement to the query. */
inline Builder &whereEqMonth(const Column &column, QVariant value,
const QString &condition = AND);
/*! Add an equal "where year" statement to the query. */
inline Builder &whereEqYear(const Column &column, QVariant value,
const QString &condition = AND);
/*! Add a "or where date" statement to the query. */
inline Builder &orWhereDate(const Column &column, const QString &comparison,
QVariant value);
/*! Add a "or where time" statement to the query. */
inline Builder &orWhereTime(const Column &column, const QString &comparison,
QVariant value);
/*! Add a "or where day" statement to the query. */
inline Builder &orWhereDay(const Column &column, const QString &comparison,
QVariant value);
/*! Add a "or where month" statement to the query. */
inline Builder &orWhereMonth(const Column &column, const QString &comparison,
QVariant value);
/*! Add a "or where year" statement to the query. */
inline Builder &orWhereYear(const Column &column, const QString &comparison,
QVariant value);
/* where raw */
/*! Add a raw "where" clause to the query. */
Builder &whereRaw(const QString &sql, const QVector<QVariant> &bindings = {},
const QString &condition = AND);
/*! Add a raw "or where" clause to the query. */
Builder &orWhereRaw(const QString &sql, const QVector<QVariant> &bindings = {});
/* Group by and having */
/*! Add a "group by" clause to the query. */
Builder &groupBy(const QVector<Column> &groups);
/*! Add a "group by" clause to the query. */
Builder &groupBy(const Column &group);
/*! Add a "group by" clause to the query. */
template<ColumnConcept ...Args>
inline Builder &groupBy(Args &&...groups);
/*! Add a raw "groupBy" clause to the query. */
Builder &groupByRaw(const QString &sql, const QVector<QVariant> &bindings = {});
/*! Add a "having" clause to the query. */
Builder &having(const Column &column, const QString &comparison,
const QVariant &value, const QString &condition = AND);
/*! Add an "or having" clause to the query. */
Builder &orHaving(const Column &column, const QString &comparison,
const QVariant &value);
/*! Add a raw "having" clause to the query. */
Builder &havingRaw(const QString &sql, const QVector<QVariant> &bindings = {},
const QString &condition = AND);
/*! Add a raw "or having" clause to the query. */
Builder &orHavingRaw(const QString &sql, const QVector<QVariant> &bindings = {});
/* Ordering */
/*! Add an "order by" clause to the query. */
Builder &orderBy(const Column &column, const QString &direction = ASC);
/*! Add a descending "order by" clause to the query. */
Builder &orderByDesc(const Column &column);
/*! Add an "order by" clause to the query with a subquery ordering. */
template<Queryable T>
Builder &orderBy(T &&query, const QString &direction = ASC);
/*! Add a descending "order by" clause to the query with a subquery ordering. */
template<Queryable T>
inline Builder &orderByDesc(T &&query);
/*! Put the query's results in random order. */
Builder &inRandomOrder(const QString &seed = "");
/*! Add a raw "order by" clause to the query. */
Builder &orderByRaw(const QString &sql, const QVector<QVariant> &bindings = {});
/*! Add an "order by" clause for a timestamp to the query. */
Builder &latest(const Column &column = CREATED_AT);
/*! Add an "order by" clause for a timestamp to the query. */
Builder &oldest(const Column &column = CREATED_AT);
/*! Remove all existing orders. */
Builder &reorder();
/*! Remove all existing orders and optionally add a new order. */
Builder &reorder(const Column &column, const QString &direction = ASC);
/*! Set the "limit" value of the query. */
Builder &limit(qint64 value);
/*! Alias to set the "limit" value of the query. */
Builder &take(qint64 value);
/*! Set the "offset" value of the query. */
Builder &offset(qint64 value);
/*! Alias to set the "offset" value of the query. */
Builder &skip(qint64 value);
/*! Set the limit and offset for a given page. */
Builder &forPage(qint64 page, qint64 perPage = 30);
/*! Constrain the query to the previous "page" of results before a given ID. */
Builder &forPageBeforeId(qint64 perPage = 30, const QVariant &lastId = {},
const QString &column = Orm::Constants::ID,
bool prependOrder = false);
/*! Constrain the query to the next "page" of results after a given ID. */
Builder &forPageAfterId(qint64 perPage = 30, const QVariant &lastId = {},
const QString &column = Orm::Constants::ID,
bool prependOrder = false);
/* Others */
/*! Increment a column's value by a given amount. */
template<typename T = std::size_t> requires std::is_arithmetic_v<T>
std::tuple<int, QSqlQuery>
increment(const QString &column, T amount = 1,
const QVector<UpdateItem> &extra = {});
/*! Decrement a column's value by a given amount. */
template<typename T = std::size_t> requires std::is_arithmetic_v<T>
std::tuple<int, QSqlQuery>
decrement(const QString &column, T amount = 1,
const QVector<UpdateItem> &extra = {});
/* Pessimistic Locking */
/*! Lock the selected rows in the table for updating. */
Builder &lockForUpdate();
/*! Share lock the selected rows in the table. */
Builder &sharedLock();
/*! Lock the selected rows in the table. */
Builder &lock(bool value = true);
/*! Lock the selected rows in the table. */
Builder &lock(const char *value);
/*! Lock the selected rows in the table. */
Builder &lock(const QString &value);
/*! Lock the selected rows in the table. */
Builder &lock(QString &&value);
/* Debugging */
/*! Dump the current SQL and bindings. */
void dump(bool replaceBindings = true, bool simpleBindings = false);
/*! Die and dump the current SQL and bindings. */
void dd(bool replaceBindings = true, bool simpleBindings = false);
/* Getters / Setters */
/*! Get the default key name of the table. */
const QString &defaultKeyName() const noexcept;
/*! Get a database connection. */
inline DatabaseConnection &getConnection() const noexcept;
/*! Get the query grammar reference. */
inline const QueryGrammar &getGrammar() const noexcept;
/*! Get a database connection as a std::shared_ptr. */
inline std::shared_ptr<DatabaseConnection> getConnectionShared() const noexcept;
/*! Get the query grammar instance as a std::shared_ptr. */
inline std::shared_ptr<QueryGrammar> getGrammarShared() const noexcept;
/*! Get the current query value bindings as flattened QVector. */
QVector<QVariant> getBindings() const;
/*! Get the raw map of bindings. */
inline const BindingsMap &getRawBindings() const noexcept;
/*! Add a binding to the query. */
Builder &addBinding(const QVariant &binding,
BindingType type = BindingType::WHERE);
/*! Add a binding to the query. */
Builder &addBinding(QVariant &&binding,
BindingType type = BindingType::WHERE);
/*! Add bindings to the query. */
Builder &addBinding(const QVector<QVariant> &bindings,
BindingType type = BindingType::WHERE);
/*! Add bindings to the query. */
Builder &addBinding(QVector<QVariant> &&bindings,
BindingType type = BindingType::WHERE);
/*! Set the bindings on the query builder. */
Builder &setBindings(QVector<QVariant> &&bindings,
BindingType type = BindingType::WHERE);
/*! Get an aggregate function and column to be run. */
inline const std::optional<AggregateItem> &getAggregate() const noexcept;
/*! Check if the query returns distinct results. */
inline const std::variant<bool, QStringList> &getDistinct() const noexcept;
/*! Check if the query returns distinct results. */
template<typename T> requires std::same_as<T, bool>
inline bool getDistinct() const;
/*! Check if the query returns distinct results. */
template<typename T> requires std::same_as<T, QStringList>
inline const QStringList &getDistinct() const;
// TODO check up all code and return references when appropriate silverqx
/*! Get the columns that should be returned. */
inline const QVector<Column> &getColumns() const noexcept;
/*! Set the columns that should be returned. */
inline Builder &setColumns(const QVector<Column> &columns) noexcept;
/*! Set the columns that should be returned. */
inline Builder &setColumns(QVector<Column> &&columns) noexcept;
/*! Get the table associated with the query builder. */
inline const FromClause &getFrom() const noexcept;
/*! Get the table joins for the query. */
inline const QVector<std::shared_ptr<JoinClause>> &getJoins() const noexcept;
/*! Get the where constraints for the query. */
inline const QVector<WhereConditionItem> &getWheres() const noexcept;
/*! Get the groupings for the query. */
inline const QVector<Column> &getGroups() const noexcept;
/*! Get the having constraints for the query. */
inline const QVector<HavingConditionItem> &getHavings() const noexcept;
/*! Get the orderings for the query. */
inline const QVector<OrderByItem> &getOrders() const noexcept;
/*! Get the maximum number of records to return. */
inline qint64 getLimit() const noexcept;
/*! Get the number of records to skip. */
inline qint64 getOffset() const noexcept;
/*! Get the row locking. */
inline const std::variant<std::monostate, bool, QString> &
getLock() const noexcept;
/* Other methods */
/*! Get a new instance of the query builder. */
virtual std::shared_ptr<Builder> newQuery() const;
/*! Create a new query instance for nested where condition. */
std::shared_ptr<Builder> forNestedWhere() const;
/*! Create a raw database expression. */
Expression raw(const QVariant &value) const;
/*! Create a raw database expression. */
Expression raw(QVariant &&value) const noexcept;
/*! Add another query builder as a nested where to the query builder. */
Builder &addNestedWhereQuery(const std::shared_ptr<Builder> &query,
const QString &condition);
/*! Add an "exists" clause to the query. */
Builder &addWhereExistsQuery(const std::shared_ptr<Builder> &query,
const QString &condition = AND, bool nope = false);
/*! Add an "exists" clause to the query. */
Builder &addWhereExistsQuery(Builder &query, const QString &condition = AND,
bool nope = false);
/*! Merge an array of where clauses and bindings. */
Builder &mergeWheres(const QVector<WhereConditionItem> &wheres,
const QVector<QVariant> &bindings);
/*! Merge an array of where clauses and bindings. */
Builder &mergeWheres(QVector<WhereConditionItem> &&wheres,
QVector<QVariant> &&bindings);
/*! Builder property types. */
enum struct PropertyType
{
COLUMNS,
};
/*! Clone the query. */
inline Builder clone() const;
/*! Clone the query without the given properties. */
Builder cloneWithout(const std::unordered_set<PropertyType> &properties) const;
/*! Clone the query without the given bindings. */
Builder cloneWithoutBindings(
const std::unordered_set<BindingType> &except) const;
protected:
/*! Throw if the given operator is not valid for the current DB connection. */
void throwIfInvalidOperator(const QString &comparison) const;
/*! Remove all of the expressions from a list of bindings. */
static QVector<QVariant> cleanBindings(const QVector<QVariant> &bindings);
/*! Remove all of the expressions from a list of bindings. */
static QVector<QVariant> cleanBindings(QVector<QVariant> &&bindings);
/*! Remove all of the expressions from the WhereBetweenItem bindings. */
static QVector<QVariant> cleanBindings(const WhereBetweenItem &bindings);
/*! Add a vector of basic where clauses to the query. */
Builder &
addArrayOfWheres(const QVector<WhereItem> &values,
const QString &condition = AND,
const QString &defaultCondition = "");
/*! Add a vector of where clauses comparing two columns to the query. */
Builder &
addArrayOfWheres(const QVector<WhereColumnItem> &values,
const QString &condition = AND);
/*! Get a new join clause. */
static std::shared_ptr<JoinClause>
newJoinClause(const Builder &query, const QString &type, const QString &table);
/*! Get a new join clause. */
static std::shared_ptr<JoinClause>
newJoinClause(const Builder &query, const QString &type, Expression &&table);
/*! Remove all existing columns and column bindings. */
Builder &clearColumns();
/*! Execute the given callback while selecting the given columns. */
SqlQuery
onceWithColumns(const QVector<Column> &columns,
const std::function<SqlQuery()> &callback);
/*! Creates a subquery and parse it. */
std::pair<QString, QVector<QVariant>>
createSub(const std::function<void(Builder &)> &callback) const;
/*! Creates a subquery and parse it. */
std::pair<QString, QVector<QVariant>>
createSub(Builder &query) const;
/*! Creates a subquery and parse it. */
static std::pair<QString, QVector<QVariant>>
createSub(const QString &query) noexcept;
/*! Creates a subquery and parse it. */
static std::pair<QString, QVector<QVariant>>
createSub(QString &&query) noexcept;
/*! Determine whether the T type is a query builder instance or a lambda expr. */
template<typename T>
constexpr static bool isQueryable =
std::is_convertible_v<T, Orm::QueryBuilder &> ||
std::is_invocable_v<T, Orm::QueryBuilder &>;
/*! Create a new query instance for a sub-query. */
inline virtual std::shared_ptr<Builder> forSubQuery() const;
/*! Prepend the database name if the given query is on another database. */
Builder &prependDatabaseNameIfCrossDatabaseQuery(Builder &query) const;
/*! Throw an exception if the query doesn't have an orderBy clause. */
void enforceOrderBy() const;
/*! Get an array with all orders with a given column removed. */
QVector<OrderByItem> removeExistingOrdersFor(const QString &column) const;
/*! Strip off the table name or alias from a column identifier. */
static QString stripTableForPluck(const Column &column);
/* Getters / Setters */
/*! Set the aggregate property without running the query. */
Builder &setAggregate(const QString &function,
const QVector<Column> &columns = {ASTERISK});
private:
/*! Run the query as a "select" statement against the connection. */
SqlQuery runSelect();
/*! Set the table which the query is targeting. */
inline Builder &setFrom(const FromClause &from);
/*! Add a join clause to the query, common code. */
Builder &joinInternal(
std::shared_ptr<JoinClause> &&join, const QString &first,
const QString &comparison, const QVariant &second, bool where);
/*! Add an advanced join clause to the query, common code. */
Builder &joinInternal(
std::shared_ptr<JoinClause> &&join,
const std::function<void(JoinClause &)> &callback);
/*! Add a join clause to the query, common code for the above two methods. */
Builder &joinInternal(std::shared_ptr<JoinClause> &&join);
/*! Add a subquery join clause to the query, common code. */
Builder &joinSubInternal(
std::pair<QString, QVector<QVariant>> &&subQuery, const QString &as,
const QString &first, const QString &comparison, const QVariant &second,
const QString &type, bool where);
/*! Add a subquery join clause to the query, common code. */
Builder &joinSubInternal(
std::pair<QString, QVector<QVariant>> &&subQuery, const QString &as,
const std::function<void(JoinClause &)> &callback,
const QString &type);
/*! Add a basic where clause to the query, common code. */
Builder &whereInternal(
const Column &column, const QString &comparison, QVariant value,
const QString &condition, WhereType type = WhereType::BASIC);
/*! Throw exception when m_bindings doesn't contain a passed type. */
void checkBindingType(BindingType type) const;
/*! All of the available clause operators. */
static const std::unordered_set<QString> &getOperators();
/*! The database connection instance. */
std::shared_ptr<DatabaseConnection> m_connection;
/*! The database query grammar instance. */
std::shared_ptr<QueryGrammar> m_grammar;
/*! The current query value bindings.
Order is crucial here because of that QMap with an enum struct is used. */
BindingsMap m_bindings {
{BindingType::SELECT, {}},
{BindingType::FROM, {}},
{BindingType::JOIN, {}},
{BindingType::WHERE, {}},
{BindingType::GROUPBY, {}},
{BindingType::HAVING, {}},
{BindingType::ORDER, {}},
{BindingType::UNION, {}},
{BindingType::UNIONORDER, {}},
};
/*! An aggregate function and column to be run. */
std::optional<AggregateItem> m_aggregate = std::nullopt;
/*! Indicates if the query returns distinct results. */
std::variant<bool, QStringList> m_distinct = false;
/*! The columns that should be returned. */
QVector<Column> m_columns {};
/*! The table which the query is targeting. */
FromClause m_from {};
/*! The table joins for the query. */
QVector<std::shared_ptr<JoinClause>> m_joins {};
/*! The where constraints for the query. */
QVector<WhereConditionItem> m_wheres {};
/*! The groupings for the query. */
QVector<Column> m_groups {};
/*! The having constraints for the query. */
QVector<HavingConditionItem> m_havings {};
/*! The orderings for the query. */
QVector<OrderByItem> m_orders {};
/*! The maximum number of records to return. */
qint64 m_limit = -1;
/*! The number of records to skip. */
qint64 m_offset = -1;
/*! Indicates whether row locking is being used. */
std::variant<std::monostate, bool, QString> m_lock {};
};
/* public */
/* Retrieving results */
SqlQuery Builder::findOr(const QVariant &id,
const std::function<void()> &callback)
{
return findOr(id, {ASTERISK}, callback);
}
template<typename R>
std::pair<SqlQuery, R>
Builder::findOr(const QVariant &id, const QVector<Column> &columns,
const std::function<R()> &callback)
{
auto query = find(id, columns);
if (query.isValid())
return {std::move(query), R {}};
/* Return invalid SqlQuery if a record was not found. Don't return
the SqlQuery() as an user can still obtain some info from the invalid
SqlQuery. */
// Optionally invoke the callback
if (callback)
return {std::move(query), std::invoke(callback)};
return {std::move(query), R {}};
}
template<typename R>
std::pair<SqlQuery, R>
Builder::findOr(const QVariant &id, const std::function<R()> &callback)
{
return findOr<R>(id, {ASTERISK}, callback);
}
template<typename T>
std::map<T, QVariant>
Builder::pluck(const Column &column, const Column &key)
{
/* First, we will need to select the results of the query accounting for the
given column / key. Once we have the results, we will be able to take
the results and get the exact data that was requested for the query. */
auto query = get({column, key});
// Empty result
if (QueryUtils::queryResultSize(query) == 0)
return {};
/* If the column is qualified with a table or have an alias, we cannot use
those directly in the "pluck" operations, we have to strip the table out or
use the alias name instead. */
const auto unqualifiedColumn = stripTableForPluck(column);
const auto unqualifiedKey = stripTableForPluck(key);
std::map<T, QVariant> result;
while (query.next())
result.try_emplace(query.value(unqualifiedKey).value<T>(),
query.value(unqualifiedColumn));
return result;
}
/* Insert, Update, Delete */
template<Remove T>
std::tuple<int, QSqlQuery> Builder::deleteRow(T &&id)
{
return remove(std::forward<T>(id));
}
template<Remove T>
std::tuple<int, QSqlQuery> Builder::remove(T &&id)
{
/* If an ID is passed to the method, we will set the where clause to check the
ID to let developers to simply and quickly remove a single row from this
database without manually specifying the "where" clauses on the query.
m_from will be wrapped in the Grammar. */
where(QStringLiteral("%1.id").arg(std::get<QString>(m_from)), EQ,
std::forward<T>(id), AND);
return remove();
}
/* Select */
quint64 Builder::count(const QVector<Column> &columns) const
{
return aggregate(QStringLiteral("count"), columns).template value<quint64>();
}
template<typename>
quint64 Builder::count(const Column &column)
{
return aggregate(QStringLiteral("count"), {column}).template value<quint64>();
}
QVariant Builder::min(const Column &column) const
{
return aggregate(QStringLiteral("min"), {column});
}
QVariant Builder::max(const Column &column) const
{
return aggregate(QStringLiteral("max"), {column});
}
QVariant Builder::sum(const Column &column) const
{
auto result = aggregate(QStringLiteral("sum"), {column});
if (!result.isValid() || result.isNull())
result = 0;
return result;
}
QVariant Builder::avg(const Column &column) const
{
return aggregate(QStringLiteral("avg"), {column});
}
QVariant Builder::average(const Column &column) const
{
return avg(column);
}
bool Builder::doesntExist()
{
return !exists();
}
template<typename R>
std::pair<bool, R> Builder::existsOr(const std::function<R()> &callback)
{
if (exists())
return {true, R {}};
return {false, std::invoke(callback)};
}
template<typename R>
std::pair<bool, R> Builder::doesntExistOr(const std::function<R()> &callback)
{
if (doesntExist())
return {true, R {}};
return {false, std::invoke(callback)};
}
template<Queryable T>
Builder &Builder::select(T &&query, const QString &as)
{
return selectSub(std::forward<T>(query), as);
}
template<Queryable T>
Builder &Builder::addSelect(T &&query, const QString &as)
{
if (m_columns.isEmpty())
select(QVector<Column> {QStringLiteral("%1.*")
.arg(std::get<QString>(m_from))});
return selectSub(std::forward<T>(query), as);
}
template<SubQuery T>
Builder &Builder::selectSub(T &&query, const QString &as)
{
const auto [queryString, bindings] = createSub(std::forward<T>(query));
return selectRaw(QStringLiteral("(%1) as %2").arg(queryString,
m_grammar->wrap(as)),
bindings);
}
template<SubQuery T>
Builder &
Builder::fromSub(T &&query, const QString &as)
{
const auto [queryString, bindings] = createSub(std::forward<T>(query));
return fromRaw(QStringLiteral("(%1) as %2").arg(queryString,
m_grammar->wrapTable(as)),
bindings);
}
/* Joins */
template<JoinTable T>
Builder &
Builder::join(T &&table, const QString &first, const QString &comparison,
const QVariant &second, const QString &type, const bool where)
{
// Ownership of the std::shared_ptr<JoinClause>
return joinInternal(newJoinClause(*this, type, std::forward<T>(table)),
first, comparison, second, where);
}
// FUTURE joinSub, missing where param, also in joinSub silverqx
template<JoinTable T>
Builder &
Builder::join(T &&table, const std::function<void(JoinClause &)> &callback,
const QString &type)
{
// Ownership of the std::shared_ptr<JoinClause>
return joinInternal(newJoinClause(*this, type, std::forward<T>(table)),
callback);
}
template<JoinTable T>
Builder &
Builder::joinWhere(T &&table, const QString &first, const QString &comparison,
const QVariant &second, const QString &type)
{
return join(std::forward<T>(table), first, comparison, second, type, true);
}
template<JoinTable T>
Builder &
Builder::leftJoin(T &&table, const QString &first, const QString &comparison,
const QVariant &second)
{
return join(std::forward<T>(table), first, comparison, second, LEFT);
}
template<JoinTable T>
Builder &
Builder::leftJoin(T &&table, const std::function<void(JoinClause &)> &callback)
{
return join(std::forward<T>(table), callback, LEFT);
}
template<JoinTable T>
Builder &
Builder::leftJoinWhere(T &&table, const QString &first, const QString &comparison,
const QVariant &second)
{
return joinWhere(std::forward<T>(table), first, comparison, second, LEFT);
}
template<JoinTable T>
Builder &
Builder::rightJoin(T &&table, const QString &first, const QString &comparison,
const QVariant &second)
{
return join(std::forward<T>(table), first, comparison, second, RIGHT);
}
template<JoinTable T>
Builder &
Builder::rightJoin(T &&table, const std::function<void(JoinClause &)> &callback)
{
return join(std::forward<T>(table), callback, RIGHT);
}
template<JoinTable T>
Builder &
Builder::rightJoinWhere(T &&table, const QString &first, const QString &comparison,
const QVariant &second)
{
return joinWhere(std::forward<T>(table), first, comparison, second, RIGHT);
}
// TODO docs missing example, because of different api silverqx
// NOTE api different silverqx
template<JoinTable T>
Builder &
Builder::crossJoin(T &&table, const QString &first, const QString &comparison,
const QVariant &second)
{
return join(std::forward<T>(table), first, comparison, second, CROSS);
}
template<JoinTable T>
Builder &
Builder::crossJoin(T &&table, const std::function<void(JoinClause &)> &callback)
{
return join(std::forward<T>(table), callback, CROSS);
}
template<JoinTable T>
Builder &
Builder::crossJoin(T &&table)
{
// No need to call joinInternal() because no bindings
m_joins << newJoinClause(*this, CROSS, std::forward<T>(table));
return *this;
}
template<SubQuery T>
Builder &
Builder::joinSub(T &&query, const QString &as, const QString &first,
const QString &comparison, const QVariant &second,
const QString &type, const bool where)
{
return joinSubInternal(createSub(std::forward<T>(query)),
as, first, comparison, second, type, where);
}
template<SubQuery T>
Builder &
Builder::joinSub(T &&query, const QString &as,
const std::function<void(JoinClause &)> &callback,
const QString &type)
{
return joinSubInternal(createSub(std::forward<T>(query)), as, callback, type);
}
template<SubQuery T>
Builder &
Builder::leftJoinSub(T &&query, const QString &as, const QString &first,
const QString &comparison, const QVariant &second)
{
return joinSub(std::forward<T>(query), as, first, comparison, second, LEFT);
}
template<SubQuery T>
Builder &
Builder::leftJoinSub(T &&query, const QString &as,
const std::function<void(JoinClause &)> &callback)
{
return joinSub(std::forward<T>(query), as, callback, LEFT);
}
template<SubQuery T>
Builder &
Builder::rightJoinSub(T &&query, const QString &as, const QString &first,
const QString &comparison, const QVariant &second)
{
return joinSub(std::forward<T>(query), as, first, comparison, second, RIGHT);
}
template<SubQuery T>
Builder &
Builder::rightJoinSub(T &&query, const QString &as,
const std::function<void(JoinClause &)> &callback)
{
return joinSub(std::forward<T>(query), as, callback, RIGHT);
}
/* Basic where */
template<WhereValue T>
Builder &
Builder::where(const Column &column, const QString &comparison, T &&value,
const QString &condition)
{
/* If the value is queryable, it means the developer is performing an entire
sub-select within the query and we will need to compile the sub-select
within the where clause to get the appropriate query record results. */
if constexpr (isQueryable<T>)
return whereSub(column, comparison, std::forward<T>(value), condition);
else
return whereInternal(column, comparison, std::forward<T>(value), condition);
}
template<WhereValue T>
Builder &
Builder::orWhere(const Column &column, const QString &comparison, T &&value)
{
return where(column, comparison, std::forward<T>(value), OR);
}
template<WhereValue T>
Builder &
Builder::whereEq(const Column &column, T &&value, const QString &condition)
{
return where(column, EQ, std::forward<T>(value), condition);
}
template<WhereValue T>
Builder &Builder::orWhereEq(const Column &column, T &&value)
{
return where(column, EQ, std::forward<T>(value), OR);
}
/* Genral where not */
template<WhereValue T>
Builder &
Builder::whereNot(const Column &column, const QString &comparison, T &&value,
const QString &condition)
{
return where(column, comparison, std::forward<T>(value),
SPACE_IN.arg(condition, NOT));
}
template<WhereValue T>
Builder &
Builder::orWhereNot(const Column &column, const QString &comparison, T &&value)
{
return where(column, comparison, std::forward<T>(value),
SPACE_IN.arg(OR, NOT));
}
template<WhereValue T>
Builder &
Builder::whereNotEq(const Column &column, T &&value, const QString &condition)
{
return where(column, EQ, std::forward<T>(value), SPACE_IN.arg(condition, NOT));
}
template<WhereValue T>
Builder &Builder::orWhereNotEq(const Column &column, T &&value)
{
return where(column, EQ, std::forward<T>(value), SPACE_IN.arg(OR, NOT));
}
/* where sub-queries */
template<Queryable C, WhereValue V>
Builder &
Builder::where(C &&column, const QString &comparison, V &&value,
const QString &condition)
{
/* If the column is a Closure instance and there is an operator value, we will
assume the developer wants to run a subquery and then compare the result
of that subquery with the given value that was provided to the method. */
// comparison operator check will be done in the where() method
auto [queryString, bindings] = createSub(std::forward<C>(column));
addBinding(std::move(bindings), BindingType::WHERE);
return where(Expression(PARENTH_ONE.arg(queryString)), comparison,
std::forward<V>(value), condition);
}
template<Queryable C, WhereValue V>
Builder &Builder::orWhere(C &&column, const QString &comparison, V &&value)
{
return where(std::forward<C>(column), comparison, std::forward<V>(value), OR);
}
template<Queryable C, WhereValue V>
Builder &Builder::whereEq(C &&column, V &&value, const QString &condition)
{
return where(std::forward<C>(column), EQ, std::forward<V>(value), condition);
}
template<Queryable C, WhereValue V>
Builder &Builder::orWhereEq(C &&column, V &&value)
{
return where(std::forward<C>(column), EQ, std::forward<V>(value), OR);
}
/* where not sub-queries */
template<Queryable C, WhereValue V>
Builder &
Builder::whereNot(C &&column, const QString &comparison, V &&value,
const QString &condition)
{
return where(std::forward<C>(column), comparison, std::forward<V>(value),
SPACE_IN.arg(condition, NOT));
}
template<Queryable C, WhereValue V>
Builder &Builder::orWhereNot(C &&column, const QString &comparison, V &&value)
{
return where(std::forward<C>(column), comparison, std::forward<V>(value),
SPACE_IN.arg(OR, NOT));
}
template<Queryable C, WhereValue V>
Builder &Builder::whereNotEq(C &&column, V &&value, const QString &condition)
{
return where(std::forward<C>(column), EQ, std::forward<V>(value),
SPACE_IN.arg(condition, NOT));
}
template<Queryable C, WhereValue V>
Builder &Builder::orWhereNotEq(C &&column, V &&value)
{
return where(std::forward<C>(column), EQ, std::forward<V>(value),
SPACE_IN.arg(OR, NOT));
}
template<WhereValueSubQuery T>
Builder &
Builder::whereSub(const Column &column, const QString &comparison,
T &&query, const QString &condition)
{
// comparison operator check will be done in the where() method
auto [queryString, bindings] = createSub(std::forward<T>(query));
addBinding(std::move(bindings), BindingType::WHERE);
return where(column, comparison, Expression(PARENTH_ONE.arg(queryString)),
condition);
}
/* where exists */
template<QueryableShared C>
Builder &Builder::whereExists(C &&callback, const QString &condition,
const bool nope)
{
if constexpr (std::invocable<C, Builder &>) {
// Ownership of the std::shared_ptr<QueryBuilder>
const auto query = forSubQuery();
/* Similar to the sub-select clause, we will create a new query instance so
the developer may cleanly specify the entire exists query and we will
compile the whole thing in the grammar and insert it into the SQL. */
std::invoke(callback, *query);
return addWhereExistsQuery(query, condition, nope);
}
// For the QueryBuilder & or std::shared_ptr<QueryBuilder>
else
return addWhereExistsQuery(callback, condition, nope);
}
template<QueryableShared C>
Builder &Builder::orWhereExists(C &&callback, const bool nope)
{
return whereExists(callback, OR, nope);
}
template<QueryableShared C>
Builder &Builder::whereNotExists(C &&callback, const QString &condition)
{
return whereExists(callback, condition, true);
}
template<QueryableShared C>
Builder &Builder::orWhereNotExists(C &&callback)
{
return whereExists(callback, OR, true);
}
/* where dates */
Builder &
Builder::whereEqDate(const Column &column, QVariant value, const QString &condition)
{
return whereDate(column, EQ, std::move(value), condition);
}
Builder &
Builder::whereEqTime(const Column &column, QVariant value, const QString &condition)
{
return whereTime(column, EQ, std::move(value), condition);
}
Builder &
Builder::whereEqDay(const Column &column, QVariant value, const QString &condition)
{
return whereDay(column, EQ, std::move(value), condition);
}
Builder &
Builder::whereEqMonth(const Column &column, QVariant value, const QString &condition)
{
return whereMonth(column, EQ, std::move(value), condition);
}
Builder &
Builder::whereEqYear(const Column &column, QVariant value, const QString &condition)
{
return whereYear(column, EQ, std::move(value), condition);
}
Builder &
Builder::orWhereDate(const Column &column, const QString &comparison, QVariant value)
{
return whereDate(column, comparison, std::move(value), OR);
}
Builder &
Builder::orWhereTime(const Column &column, const QString &comparison, QVariant value)
{
return whereTime(column, comparison, std::move(value), OR);
}
Builder &
Builder::orWhereDay(const Column &column, const QString &comparison, QVariant value)
{
return whereDay(column, comparison, std::move(value), OR);
}
Builder &
Builder::orWhereMonth(const Column &column, const QString &comparison, QVariant value)
{
return whereMonth(column, comparison, std::move(value), OR);
}
Builder &
Builder::orWhereYear(const Column &column, const QString &comparison, QVariant value)
{
return whereYear(column, comparison, std::move(value), OR);
}
template<ColumnConcept ...Args>
Builder &Builder::groupBy(Args &&...groups)
{
return groupBy(QVector<Column> {std::forward<Args>(groups)...});
}
template<Queryable T>
Builder &Builder::orderBy(T &&query, const QString &direction)
{
auto [queryString, bindings] = createSub(std::forward<T>(query));
addBinding(std::move(bindings), BindingType::ORDER);
return orderBy(Expression(PARENTH_ONE.arg(queryString)), direction);
}
template<Queryable T>
Builder &Builder::orderByDesc(T &&query)
{
return orderBy(std::forward<T>(query), DESC);
}
/* Others */
template<typename T> requires std::is_arithmetic_v<T>
std::tuple<int, QSqlQuery>
Builder::increment(const QString &column, const T amount,
const QVector<UpdateItem> &extra)
{
const auto expression = QStringLiteral("%1 + %2").arg(m_grammar->wrap(column))
.arg(amount);
QVector<UpdateItem> columns {{column, raw(expression)}};
std::copy(extra.cbegin(), extra.cend(), std::back_inserter(columns));
return update(columns);
}
template<typename T> requires std::is_arithmetic_v<T>
std::tuple<int, QSqlQuery>
Builder::decrement(const QString &column, const T amount,
const QVector<UpdateItem> &extra)
{
const auto expression = QStringLiteral("%1 - %2").arg(m_grammar->wrap(column))
.arg(amount);
QVector<UpdateItem> columns {{column, raw(expression)}};
std::copy(extra.cbegin(), extra.cend(), std::back_inserter(columns));
return update(columns);
}
/* Getters / Setters */
DatabaseConnection &Builder::getConnection() const noexcept
{
return *m_connection;
}
const Builder::QueryGrammar &Builder::getGrammar() const noexcept
{
return *m_grammar;
}
std::shared_ptr<DatabaseConnection> Builder::getConnectionShared() const noexcept
{
return m_connection;
}
std::shared_ptr<Builder::QueryGrammar> Builder::getGrammarShared() const noexcept
{
return m_grammar;
}
const BindingsMap &Builder::getRawBindings() const noexcept
{
return m_bindings;
}
const std::optional<AggregateItem> &Builder::getAggregate() const noexcept
{
return m_aggregate;
}
const std::variant<bool, QStringList> &Builder::getDistinct() const noexcept
{
return m_distinct;
}
template<typename T> requires std::same_as<T, bool>
bool Builder::getDistinct() const
{
return std::get<bool>(m_distinct);
}
template<typename T> requires std::same_as<T, QStringList>
const QStringList &
Builder::getDistinct() const
{
return std::get<QStringList>(m_distinct);
}
const QVector<Column> &
Builder::getColumns() const noexcept
{
return m_columns;
}
Builder &
Builder::setColumns(const QVector<Column> &columns) noexcept
{
m_columns = columns;
return *this;
}
Builder &
Builder::setColumns(QVector<Column> &&columns) noexcept
{
m_columns = std::move(columns);
return *this;
}
const FromClause &
Builder::getFrom() const noexcept
{
return m_from;
}
const QVector<std::shared_ptr<JoinClause>> &
Builder::getJoins() const noexcept
{
return m_joins;
}
const QVector<WhereConditionItem> &
Builder::getWheres() const noexcept
{
return m_wheres;
}
const QVector<Column> &
Builder::getGroups() const noexcept
{
return m_groups;
}
const QVector<HavingConditionItem> &
Builder::getHavings() const noexcept
{
return m_havings;
}
const QVector<OrderByItem> &
Builder::getOrders() const noexcept
{
return m_orders;
}
qint64 Builder::getLimit() const noexcept
{
return m_limit;
}
qint64 Builder::getOffset() const noexcept
{
return m_offset;
}
const std::variant<std::monostate, bool, QString> &
Builder::getLock() const noexcept
{
return m_lock;
}
Builder Builder::clone() const
{
return *this;
}
/* protected */
std::shared_ptr<Builder>
Builder::forSubQuery() const
{
return newQuery();
}
/* private */
Builder &
Builder::setFrom(const FromClause &from)
{
m_from = from;
return *this;
}
} // namespace Orm::Query
TINYORM_END_COMMON_NAMESPACE
#endif // ORM_QUERY_QUERYBUILDER_HPP