added whereBetween and whereBetweenColumns

- added proxies
 - added unit and functional tests
 - added docs
 - updated number of unit tests to 1417
This commit is contained in:
silverqx
2022-08-18 11:12:48 +02:00
parent 44b302578c
commit d54b041d81
18 changed files with 794 additions and 20 deletions

View File

@@ -74,7 +74,7 @@ Whole library is documented as markdown documents:
- the `tom` console application with tab completion for all shells (pwsh, bash, zsh) 🥳
- scaffolding of models, migrations, and seeders
- overhauled models scaffolding, every feature that is supported by models can be generated using the `tom make:model` cli command
- a huge amount of code is unit tested, currently __1408 unit tests__ 🤯
- a huge amount of code is unit tested, currently __1417 unit tests__ 🤯
- C++20 only, with all the latest features used like concepts/constraints, ranges, smart pointers (no `new` keyword in the whole code 😎), folding expressions
- qmake and CMake build systems support
- CMake FetchContent module support 🤙

View File

@@ -482,6 +482,38 @@ The `whereNot` and `orWhereNot` methods may be used to negate a given group of q
### Additional Where Clauses
**whereBetween / orWhereBetween**
The `whereBetween` method verifies that a column's value is between two values:
auto users = DB::table("users")
->whereBetween("votes", {1, 100})
.get();
**whereNotBetween / orWhereNotBetween**
The `whereNotBetween` method verifies that a column's value lies outside of two values:
auto users = DB::table("users")
->whereNotBetween("votes", {1, 100})
.get();
**whereBetweenColumns / orWhereBetweenColumns**
The `whereBetweenColumns` method verifies that a column's value is between two values in given columns:
auto files = DB::table("files")
->whereBetweenColumns("quota", {"min_allowed_quota", "max_allowed_quota"})
.get();
**whereNotBetweenColumns / orWhereNotBetweenColumns**
The `whereNotBetweenColumns` method verifies that a column's value lies outside of two values in given columns:
auto files = DB::table("files")
->whereNotBetweenColumns("quota", {"min_allowed_quota", "max_allowed_quota"})
.get();
**whereIn / whereNotIn / orWhereIn / orWhereNotIn**
The `whereIn` method verifies that a given column's value is contained within the given `QVector<QVariant>`:

View File

@@ -46,7 +46,7 @@ The following list fastly summarizes all `TinyORM` features.
- the `tom` console application with tab completion for all shells (pwsh, bash, zsh) 🥳
- scaffolding of models, migrations, and seeders
- overhauled models scaffolding, every feature that is supported by models can be generated using the `tom make:model` cli command
- a huge amount of code is unit tested, currently __1408 unit tests__ 🤯
- a huge amount of code is unit tested, currently __1417 unit tests__ 🤯
- C++20 only, with all the latest features used like concepts/constraints, ranges, smart pointers (no `new` keyword in the whole code 😎), folding expressions
- qmake and CMake build systems support
- vcpkg support (also the vcpkg port, currently not committed to the vcpkg repository ☹️)

View File

@@ -8,7 +8,7 @@ keywords: [c++ orm, supported compilers, supported build systems, tinyorm]
# Supported Compilers
Following compilers are backed up by the GitHub Action [workflows](https://github.com/silverqx/TinyORM/tree/main/.github/workflows) (CI pipelines), these workflows also include more then __1408 unit tests__ 😮💥.
Following compilers are backed up by the GitHub Action [workflows](https://github.com/silverqx/TinyORM/tree/main/.github/workflows) (CI pipelines), these workflows also include more then __1417 unit tests__ 😮💥.
<div id="supported-compilers">

View File

@@ -79,21 +79,8 @@ namespace Query
EXISTS,
NOT_EXISTS,
ROW_VALUES,
};
/*! Where clause item, primarily used in grammars to build sql query. */
struct WhereConditionItem
{
Column column {};
QVariant value {};
QString comparison {Orm::Constants::EQ};
QString condition {Orm::Constants::AND};
WhereType type {WhereType::UNDEFINED};
std::shared_ptr<QueryBuilder> nestedQuery {nullptr};
QVector<Column> columns {};
QVector<QVariant> values {};
Column columnTwo {};
QString sql {}; // for the raw version
BETWEEN,
BETWEEN_COLUMNS,
};
/*! Supported having types. */
@@ -148,6 +135,38 @@ namespace Query
QString condition {};
};
/*! Where item that stores values for the where between clause. */
struct WhereBetweenItem
{
QVariant min {};
QVariant max {};
};
/*! Where item that stores column names for the where between clause. */
struct WhereBetweenColumnsItem
{
Column min {};
Column max {};
};
/*! Where clause item, primarily used in grammars to build sql query. */
struct WhereConditionItem
{
Column column {};
QVariant value {};
QString comparison {Orm::Constants::EQ};
QString condition {Orm::Constants::AND};
WhereType type {WhereType::UNDEFINED};
std::shared_ptr<QueryBuilder> nestedQuery {nullptr};
QVector<Column> columns {};
QVector<QVariant> values {};
Column columnTwo {};
QString sql {}; // for the raw version
bool nope {false};
WhereBetweenItem between {};
WhereBetweenColumnsItem betweenColumns {};
};
} // namespace Orm
TINYORM_END_COMMON_NAMESPACE

View File

@@ -182,6 +182,10 @@ namespace Orm::Query::Grammars
QString whereNotExists(const WhereConditionItem &where) const;
/*! Compile a where row values condition. */
QString whereRowValues(const WhereConditionItem &where) const;
/*! Compile a "between" where clause. */
QString whereBetween(const WhereConditionItem &where) const;
/*! Compile a "between" where clause using columns. */
QString whereBetweenColumns(const WhereConditionItem &where) const;
/*! Compile a insert values lists. */
QStringList compileInsertToVector(const QVector<QVariantMap> &values) const;

View File

@@ -25,7 +25,7 @@ namespace Orm::Query
concept Remove = std::convertible_to<T, quint64> ||
std::same_as<T, Query::Expression>;
// TODO querybuilder, whereDay/Month/..., whereBetween, whereFullText silverqx
// TODO querybuilder, whereDay/Month/..., whereFullText silverqx
// FUTURE querybuilder, paginator silverqx
/*! Database query builder. */
class SHAREDLIB_EXPORT Builder : public Concerns::BuildsQueries // clazy:exclude=copyable-polymorphic
@@ -453,6 +453,34 @@ namespace Orm::Query
/*! 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>
@@ -738,6 +766,8 @@ namespace Orm::Query
QVector<QVariant> cleanBindings(const QVector<QVariant> &bindings) const;
/*! Remove all of the expressions from a list of bindings. */
QVector<QVariant> cleanBindings(QVector<QVariant> &&bindings) const;
/*! Remove all of the expressions from the WhereBetweenItem bindings. */
QVector<QVariant> cleanBindings(const WhereBetweenItem &bindings) const;
/*! Add a vector of basic where clauses to the query. */
Builder &

View File

@@ -31,7 +31,6 @@ namespace Orm::Tiny
// TODO model missing methods Model::replicate() silverqx
// TODO model missing methods Comparing Models silverqx
// TODO model missing methods Model::loadMissing() silverqx
// TODO model missing methods Model::whereBetween() silverqx
// TODO model missing methods EloquentCollection::toQuery() silverqx
// TODO model missing saveOrFail(), updateOrFail(), deleteOrFail(), I will need to implement ManagesTransaction::transaction(callback) method silverqx
/*! Base model class. */

View File

@@ -550,6 +550,42 @@ namespace Tiny
static std::unique_ptr<TinyBuilder<Derived>>
orWhereNotNull(const Column &column);
/* where between */
/*! Add a "where between" statement to the query. */
static std::unique_ptr<TinyBuilder<Derived>>
whereBetween(const Column &column, const WhereBetweenItem &values,
const QString &condition = AND, bool nope = false);
/*! Add an "or where between" statement to the query. */
static std::unique_ptr<TinyBuilder<Derived>>
orWhereBetween(const Column &column, const WhereBetweenItem &values);
/*! Add a "where not between" statement to the query. */
static std::unique_ptr<TinyBuilder<Derived>>
whereNotBetween(const Column &column, const WhereBetweenItem &values,
const QString &condition = AND);
/*! Add an "or where not between" statement to the query. */
static std::unique_ptr<TinyBuilder<Derived>>
orWhereNotBetween(const Column &column, const WhereBetweenItem &values);
/* where between columns */
/*! Add a "where between" statement using columns to the query. */
static std::unique_ptr<TinyBuilder<Derived>>
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. */
static std::unique_ptr<TinyBuilder<Derived>>
orWhereBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns);
/*! Add a "where not between" statement using columns to the query. */
static std::unique_ptr<TinyBuilder<Derived>>
whereNotBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns,
const QString &condition = AND);
/*! Add an "or where not between" statement using columns to the query. */
static std::unique_ptr<TinyBuilder<Derived>>
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>
@@ -2305,6 +2341,110 @@ namespace Tiny
return builder;
}
/* where between */
template<typename Derived, AllRelationsConcept ...AllRelations>
std::unique_ptr<TinyBuilder<Derived>>
ModelProxies<Derived, AllRelations...>::whereBetween(
const Column &column, const WhereBetweenItem &values,
const QString &condition, const bool nope)
{
auto builder = query();
builder->whereBetween(column, values, condition, nope);
return builder;
}
template<typename Derived, AllRelationsConcept ...AllRelations>
std::unique_ptr<TinyBuilder<Derived>>
ModelProxies<Derived, AllRelations...>::orWhereBetween(
const Column &column, const WhereBetweenItem &values)
{
auto builder = query();
builder->whereBetween(column, values, OR);
return builder;
}
template<typename Derived, AllRelationsConcept ...AllRelations>
std::unique_ptr<TinyBuilder<Derived>>
ModelProxies<Derived, AllRelations...>::whereNotBetween(
const Column &column, const WhereBetweenItem &values,
const QString &condition)
{
auto builder = query();
builder->whereBetween(column, values, condition, true);
return builder;
}
template<typename Derived, AllRelationsConcept ...AllRelations>
std::unique_ptr<TinyBuilder<Derived>>
ModelProxies<Derived, AllRelations...>::orWhereNotBetween(
const Column &column, const WhereBetweenItem &values)
{
auto builder = query();
builder->whereBetween(column, values, OR, true);
return builder;
}
/* where between columns */
template<typename Derived, AllRelationsConcept ...AllRelations>
std::unique_ptr<TinyBuilder<Derived>>
ModelProxies<Derived, AllRelations...>::whereBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns,
const QString &condition, const bool nope)
{
auto builder = query();
builder->whereBetweenColumns(column, betweenColumns, condition, nope);
return builder;
}
template<typename Derived, AllRelationsConcept ...AllRelations>
std::unique_ptr<TinyBuilder<Derived>>
ModelProxies<Derived, AllRelations...>::orWhereBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns)
{
auto builder = query();
builder->whereBetweenColumns(column, betweenColumns, OR);
return builder;
}
template<typename Derived, AllRelationsConcept ...AllRelations>
std::unique_ptr<TinyBuilder<Derived>>
ModelProxies<Derived, AllRelations...>::whereNotBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns,
const QString &condition)
{
auto builder = query();
builder->whereBetweenColumns(column, betweenColumns, condition, true);
return builder;
}
template<typename Derived, AllRelationsConcept ...AllRelations>
std::unique_ptr<TinyBuilder<Derived>>
ModelProxies<Derived, AllRelations...>::orWhereNotBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns)
{
auto builder = query();
builder->whereBetweenColumns(column, betweenColumns, OR, true);
return builder;
}
/* where sub-queries */
template<typename Derived, AllRelationsConcept ...AllRelations>

View File

@@ -543,6 +543,40 @@ namespace Tiny::Relations
/*! Add an "or where not null" clause to the query. */
const Relation<Model, Related> &orWhereNotNull(const Column &column) const;
/* where between */
/*! Add a "where between" statement to the query. */
const Relation<Model, Related> &whereBetween(
const Column &column, const WhereBetweenItem &values,
const QString &condition = AND, bool nope = false) const;
/*! Add an "or where between" statement to the query. */
const Relation<Model, Related> &orWhereBetween(
const Column &column, const WhereBetweenItem &values) const;
/*! Add a "where not between" statement to the query. */
const Relation<Model, Related> &whereNotBetween(
const Column &column, const WhereBetweenItem &values,
const QString &condition = AND) const;
/*! Add an "or where not between" statement to the query. */
const Relation<Model, Related> &orWhereNotBetween(
const Column &column, const WhereBetweenItem &values) const;
/* where between columns */
/*! Add a "where between" statement using columns to the query. */
const Relation<Model, Related> &whereBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns,
const QString &condition = AND, bool nope = false) const;
/*! Add an "or where between" statement using columns to the query. */
const Relation<Model, Related> &orWhereBetweenColumns(
const Column &column,
const WhereBetweenColumnsItem &betweenColumns) const;
/*! Add a "where not between" statement using columns to the query. */
const Relation<Model, Related> &whereNotBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns,
const QString &condition = AND) const;
/*! Add an "or where not between" statement using columns to the query. */
const Relation<Model, Related> &orWhereNotBetweenColumns(
const Column &column,
const WhereBetweenColumnsItem &betweenColumns) const;
/* where sub-queries */
/*! Add a basic where clause to the query with a full sub-select column. */
template<Queryable C, WhereValue V>
@@ -2140,6 +2174,94 @@ namespace Tiny::Relations
return relation();
}
/* where between */
template<class Model, class Related>
const Relation<Model, Related> &
RelationProxies<Model, Related>::whereBetween(
const Column &column, const WhereBetweenItem &values,
const QString &condition, const bool nope) const
{
getQuery().whereBetween(column, values, condition, nope);
return relation();
}
template<class Model, class Related>
const Relation<Model, Related> &
RelationProxies<Model, Related>::orWhereBetween(
const Column &column, const WhereBetweenItem &values) const
{
getQuery().whereBetween(column, values, OR);
return relation();
}
template<class Model, class Related>
const Relation<Model, Related> &
RelationProxies<Model, Related>::whereNotBetween(
const Column &column, const WhereBetweenItem &values,
const QString &condition) const
{
getQuery().whereBetween(column, values, condition, true);
return relation();
}
template<class Model, class Related>
const Relation<Model, Related> &
RelationProxies<Model, Related>::orWhereNotBetween(
const Column &column, const WhereBetweenItem &values) const
{
getQuery().whereBetween(column, values, OR, true);
return relation();
}
/* where between columns */
template<class Model, class Related>
const Relation<Model, Related> &
RelationProxies<Model, Related>::whereBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns,
const QString &condition, const bool nope) const
{
getQuery().whereBetweenColumns(column, betweenColumns, condition, nope);
return relation();
}
template<class Model, class Related>
const Relation<Model, Related> &
RelationProxies<Model, Related>::orWhereBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns) const
{
getQuery().whereBetweenColumns(column, betweenColumns, OR);
return relation();
}
template<class Model, class Related>
const Relation<Model, Related> &
RelationProxies<Model, Related>::whereNotBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns,
const QString &condition) const
{
getQuery().whereBetweenColumns(column, betweenColumns, condition, true);
return relation();
}
template<class Model, class Related>
const Relation<Model, Related> &
RelationProxies<Model, Related>::orWhereNotBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns) const
{
getQuery().whereBetweenColumns(column, betweenColumns, OR, true);
return relation();
}
/* where sub-queries */
template<class Model, class Related>

View File

@@ -382,6 +382,38 @@ namespace Tiny
/*! Add an "or where not null" clause to the query. */
TinyBuilder<Model> &orWhereNotNull(const Column &column);
/* where between */
/*! Add a "where between" statement to the query. */
TinyBuilder<Model> &whereBetween(
const Column &column, const WhereBetweenItem &values,
const QString &condition = AND, bool nope = false);
/*! Add an "or where between" statement to the query. */
TinyBuilder<Model> &orWhereBetween(
const Column &column, const WhereBetweenItem &values);
/*! Add a "where not between" statement to the query. */
TinyBuilder<Model> &whereNotBetween(
const Column &column, const WhereBetweenItem &values,
const QString &condition = AND);
/*! Add an "or where not between" statement to the query. */
TinyBuilder<Model> &orWhereNotBetween(
const Column &column, const WhereBetweenItem &values);
/* where between columns */
/*! Add a "where between" statement using columns to the query. */
TinyBuilder<Model> &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. */
TinyBuilder<Model> &orWhereBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns);
/*! Add a "where not between" statement using columns to the query. */
TinyBuilder<Model> &whereNotBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns,
const QString &condition = AND);
/*! Add an "or where not between" statement using columns to the query. */
TinyBuilder<Model> &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>
@@ -1436,6 +1468,86 @@ namespace Tiny
return builder();
}
/* where between columns */
template<typename Model>
TinyBuilder<Model> &
BuilderProxies<Model>::whereBetween(
const Column &column, const WhereBetweenItem &values,
const QString &condition, const bool nope)
{
toBase().whereBetween(column, values, condition, nope);
return builder();
}
template<typename Model>
TinyBuilder<Model> &
BuilderProxies<Model>::orWhereBetween(
const Column &column, const WhereBetweenItem &values)
{
toBase().whereBetween(column, values, OR);
return builder();
}
template<typename Model>
TinyBuilder<Model> &
BuilderProxies<Model>::whereNotBetween(
const Column &column, const WhereBetweenItem &values,
const QString &condition)
{
toBase().whereBetween(column, values, condition, true);
return builder();
}
template<typename Model>
TinyBuilder<Model> &
BuilderProxies<Model>::orWhereNotBetween(
const Column &column, const WhereBetweenItem &values)
{
toBase().whereBetween(column, values, OR, true);
return builder();
}
/* where between columns */
template<typename Model>
TinyBuilder<Model> &
BuilderProxies<Model>::whereBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns,
const QString &condition, const bool nope)
{
toBase().whereBetweenColumns(column, betweenColumns, condition, nope);
return builder();
}
template<typename Model>
TinyBuilder<Model> &
BuilderProxies<Model>::orWhereBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns)
{
toBase().whereBetweenColumns(column, betweenColumns, OR);
return builder();
}
template<typename Model>
TinyBuilder<Model> &
BuilderProxies<Model>::whereNotBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns,
const QString &condition)
{
toBase().whereBetweenColumns(column, betweenColumns, condition, true);
return builder();
}
template<typename Model>
TinyBuilder<Model> &
BuilderProxies<Model>::orWhereNotBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns)
{
toBase().whereBetweenColumns(column, betweenColumns, OR, true);
return builder();
}
/* where sub-queries */
template<typename Model>

View File

@@ -449,6 +449,28 @@ QString Grammar::whereRowValues(const WhereConditionItem &where) const
parametrize(where.values));
}
QString Grammar::whereBetween(const WhereConditionItem &where) const
{
auto between = where.nope ? QStringLiteral("not between")
: QStringLiteral("between");
return QStringLiteral("%1 %2 %3 and %4").arg(wrap(where.column),
std::move(between),
parameter(where.between.min),
parameter(where.between.max));
}
QString Grammar::whereBetweenColumns(const WhereConditionItem &where) const
{
auto between = where.nope ? QStringLiteral("not between")
: QStringLiteral("between");
return QStringLiteral("%1 %2 %3 and %4").arg(wrap(where.column),
std::move(between),
wrap(where.betweenColumns.min),
wrap(where.betweenColumns.max));
}
QStringList
Grammar::compileInsertToVector(const QVector<QVariantMap> &values) const
{

View File

@@ -216,6 +216,8 @@ MySqlGrammar::getWhereMethod(const WhereType whereType) const
bind(&MySqlGrammar::whereExists),
bind(&MySqlGrammar::whereNotExists),
bind(&MySqlGrammar::whereRowValues),
bind(&MySqlGrammar::whereBetween),
bind(&MySqlGrammar::whereBetweenColumns),
};
T_THREAD_LOCAL

View File

@@ -200,6 +200,8 @@ PostgresGrammar::getWhereMethod(const WhereType whereType) const
bind(&PostgresGrammar::whereExists),
bind(&PostgresGrammar::whereNotExists),
bind(&PostgresGrammar::whereRowValues),
bind(&PostgresGrammar::whereBetween),
bind(&PostgresGrammar::whereBetweenColumns),
};
T_THREAD_LOCAL

View File

@@ -176,6 +176,8 @@ SQLiteGrammar::getWhereMethod(const WhereType whereType) const
bind(&SQLiteGrammar::whereExists),
bind(&SQLiteGrammar::whereNotExists),
bind(&SQLiteGrammar::whereRowValues),
bind(&SQLiteGrammar::whereBetween),
bind(&SQLiteGrammar::whereBetweenColumns),
};
T_THREAD_LOCAL

View File

@@ -700,6 +700,68 @@ Builder &Builder::orWhereNotNull(const Column &column)
return orWhereNotNull(QVector<Column> {column});
}
/* where between */
Builder &Builder::whereBetween(const Column &column, const WhereBetweenItem &values,
const QString &condition, const bool nope)
{
m_wheres.append({.column = column, .condition = condition,
.type = WhereType::BETWEEN, .nope = nope,
.between = values});
addBinding(cleanBindings(values), BindingType::WHERE);
return *this;
}
Builder &Builder::orWhereBetween(const Column &column, const WhereBetweenItem &values)
{
return whereBetween(column, values, OR);
}
Builder &Builder::whereNotBetween(const Column &column, const WhereBetweenItem &values,
const QString &condition)
{
return whereBetween(column, values, condition, true);
}
Builder &Builder::orWhereNotBetween(const Column &column, const WhereBetweenItem &values)
{
return whereBetween(column, values, OR, true);
}
/* where between columns */
Builder &Builder::whereBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns,
const QString &condition, const bool nope)
{
m_wheres.append({.column = column, .condition = condition,
.type = WhereType::BETWEEN_COLUMNS, .nope = nope,
.betweenColumns = betweenColumns});
return *this;
}
Builder &Builder::orWhereBetweenColumns(const Column &column,
const WhereBetweenColumnsItem &betweenColumns)
{
return whereBetweenColumns(column, betweenColumns, OR);
}
Builder &Builder::whereNotBetweenColumns(
const Column &column, const WhereBetweenColumnsItem &betweenColumns,
const QString &condition)
{
return whereBetweenColumns(column, betweenColumns, condition, true);
}
Builder &Builder::orWhereNotBetweenColumns(const Column &column,
const WhereBetweenColumnsItem &betweenColumns)
{
return whereBetweenColumns(column, betweenColumns, OR, true);
}
/* where exists */
Builder &Builder::whereExists(
@@ -1283,6 +1345,24 @@ QVector<QVariant> Builder::cleanBindings(QVector<QVariant> &&bindings) const
return cleanedBindings;
}
QVector<QVariant> Builder::cleanBindings(const WhereBetweenItem &bindings) const
{
QVector<QVariant> cleanedBindings;
cleanedBindings.reserve(2);
if (const auto &min = bindings.min;
!min.canConvert<Expression>()
)
cleanedBindings << min;
if (const auto &max = bindings.max;
!max.canConvert<Expression>()
)
cleanedBindings << max;
return cleanedBindings;
}
Builder &
Builder::addArrayOfWheres(const QVector<WhereItem> &values, const QString &condition,
const QString &defaultCondition)

View File

@@ -52,6 +52,8 @@ private Q_SLOTS:
void implode_EmptyResult() const;
void implode_QualifiedColumnOrKey() const;
void whereBetween() const;
void updateOrInsert() const;
void updateOrInsert_EmptyValues() const;
@@ -460,6 +462,29 @@ void tst_QueryBuilder::implode_QualifiedColumnOrKey() const
}
}
void tst_QueryBuilder::whereBetween() const
{
QFETCH_GLOBAL(QString, connection);
auto result = createQuery(connection)->from("torrents")
.whereBetween("size", {12, 14})
.orderBy(ID)
.get();
QVERIFY(result.isActive() && result.isSelect() && !result.isValid());
QVERIFY(QueryUtils::queryResultSize(result) == 3);
QVector<quint64> expectedIds {2, 3, 4};
QVector<quint64> actualIds;
actualIds.reserve(expectedIds.size());
while (result.next())
actualIds << result.value("id").value<quint64>();
QCOMPARE(actualIds, expectedIds);
}
void tst_QueryBuilder::updateOrInsert() const
{
QFETCH_GLOBAL(QString, connection);

View File

@@ -176,6 +176,14 @@ private Q_SLOTS:
void whereNull_WithVectorValue() const;
void whereNotNull_WithVectorValue() const;
void whereBetween() const;
void whereNotBetween() const;
void whereBetween_ColumnExpression() const;
void whereBetweenColumns() const;
void whereNotBetweenColumns() const;
void whereBetweenColumns_ColumnExpression() const;
void whereExists() const;
void whereNotExists() const;
void orWhereExists() const;
@@ -2427,6 +2435,181 @@ void tst_MySql_QueryBuilder::whereNotNull_WithVectorValue() const
}
}
void tst_MySql_QueryBuilder::whereBetween() const
{
{
auto builder = createQuery();
builder->select("*").from("torrents").whereBetween("size", {12, 14});
QCOMPARE(builder->toSql(),
"select * from `torrents` where `size` between ? and ?");
QCOMPARE(builder->getBindings(),
QVector<QVariant>({QVariant(12), QVariant(14)}));
}
{
auto builder = createQuery();
builder->select("*").from("torrents").where(ID, GT, 2)
.orWhereBetween("size", {12, 14});
QCOMPARE(builder->toSql(),
"select * from `torrents` where `id` > ? or `size` between ? and ?");
QCOMPARE(builder->getBindings(),
QVector<QVariant>({QVariant(2), QVariant(12), QVariant(14)}));
}
}
void tst_MySql_QueryBuilder::whereNotBetween() const
{
{
auto builder = createQuery();
builder->select("*").from("torrents").whereNotBetween("size", {12, 14});
QCOMPARE(builder->toSql(),
"select * from `torrents` where `size` not between ? and ?");
QCOMPARE(builder->getBindings(),
QVector<QVariant>({QVariant(12), QVariant(14)}));
}
{
auto builder = createQuery();
builder->select("*").from("torrents").where(ID, GT, 2)
.orWhereNotBetween("size", {12, 14});
QCOMPARE(builder->toSql(),
"select * from `torrents` where `id` > ? or `size` not between ? and ?");
QCOMPARE(builder->getBindings(),
QVector<QVariant>({QVariant(2), QVariant(12), QVariant(14)}));
}
}
void tst_MySql_QueryBuilder::whereBetween_ColumnExpression() const
{
// min. value as expression
{
auto builder = createQuery();
builder->select("*").from("torrents").where(ID, GT, 2)
.orWhereBetween("size", {DB::raw(12), 14});
QCOMPARE(builder->toSql(),
"select * from `torrents` where `id` > ? or `size` between 12 and ?");
QCOMPARE(builder->getBindings(),
QVector<QVariant>({QVariant(2), QVariant(14)}));
}
// max. value as expression
{
auto builder = createQuery();
builder->select("*").from("torrents").where(ID, GT, 2)
.orWhereBetween("size", {12, DB::raw(14)});
QCOMPARE(builder->toSql(),
"select * from `torrents` where `id` > ? or `size` between ? and 14");
QCOMPARE(builder->getBindings(),
QVector<QVariant>({QVariant(2), QVariant(12)}));
}
// Both min. and max. values as expressions
{
auto builder = createQuery();
builder->select("*").from("torrents").where(ID, GT, 2)
.orWhereBetween("size", {DB::raw(12), DB::raw(14)});
QCOMPARE(builder->toSql(),
"select * from `torrents` where `id` > ? or `size` between 12 and 14");
QCOMPARE(builder->getBindings(),
QVector<QVariant>({QVariant(2)}));
}
}
void tst_MySql_QueryBuilder::whereBetweenColumns() const
{
{
auto builder = createQuery();
builder->select("*").from("torrents")
.whereBetweenColumns("size", {"min", "max"});
QCOMPARE(builder->toSql(),
"select * from `torrents` where `size` between `min` and `max`");
QVERIFY(builder->getBindings().isEmpty());
}
{
auto builder = createQuery();
builder->select("*").from("torrents").where(ID, GT, 2)
.orWhereBetweenColumns("size", {"min", "max"});
QCOMPARE(builder->toSql(),
"select * from `torrents` where `id` > ? or "
"`size` between `min` and `max`");
QCOMPARE(builder->getBindings(),
QVector<QVariant>({QVariant(2)}));
}
}
void tst_MySql_QueryBuilder::whereNotBetweenColumns() const
{
{
auto builder = createQuery();
builder->select("*").from("torrents")
.whereNotBetweenColumns("size", {"min", "max"});
QCOMPARE(builder->toSql(),
"select * from `torrents` where `size` not between `min` and `max`");
QVERIFY(builder->getBindings().isEmpty());
}
{
auto builder = createQuery();
builder->select("*").from("torrents").where(ID, GT, 2)
.orWhereNotBetweenColumns("size", {"min", "max"});
QCOMPARE(builder->toSql(),
"select * from `torrents` where `id` > ? or "
"`size` not between `min` and `max`");
QCOMPARE(builder->getBindings(),
QVector<QVariant>({QVariant(2)}));
}
}
void tst_MySql_QueryBuilder::whereBetweenColumns_ColumnExpression() const
{
// min. column name as expression
{
auto builder = createQuery();
builder->select("*").from("torrents").where(ID, GT, 2)
.orWhereBetweenColumns("size", {DB::raw("min"), "max"});
QCOMPARE(builder->toSql(),
"select * from `torrents` where `id` > ? or "
"`size` between min and `max`");
QCOMPARE(builder->getBindings(),
QVector<QVariant>({QVariant(2)}));
}
// max. column name as expression
{
auto builder = createQuery();
builder->select("*").from("torrents").where(ID, GT, 2)
.orWhereBetweenColumns("size", {"min", DB::raw("max")});
QCOMPARE(builder->toSql(),
"select * from `torrents` where `id` > ? or "
"`size` between `min` and max");
QCOMPARE(builder->getBindings(),
QVector<QVariant>({QVariant(2)}));
}
// Both min. and max. column names as expressions
{
auto builder = createQuery();
builder->select("*").from("torrents").where(ID, GT, 2)
.orWhereBetweenColumns("size", {DB::raw("min"), DB::raw("max")});
QCOMPARE(builder->toSql(),
"select * from `torrents` where `id` > ? or "
"`size` between min and max");
QCOMPARE(builder->getBindings(),
QVector<QVariant>({QVariant(2)}));
}
}
void tst_MySql_QueryBuilder::whereExists() const
{
auto builder = createQuery();