mirror of
https://github.com/silverqx/TinyORM.git
synced 2026-04-27 03:59:10 -05:00
added QueryBuilder::updateOrInsert()
- added tests - updated docs
This commit is contained in:
@@ -717,6 +717,18 @@ In addition to inserting records into the database, the query builder can also u
|
||||
An `update` and `delete` are affecting statements, so they return `std::tuple<int, QSqlQuery>`.
|
||||
:::
|
||||
|
||||
#### Update Or Insert
|
||||
|
||||
Sometimes you may want to update an existing record in the database or create it if no matching record exists. In this scenario, the `updateOrInsert` method may be used. The `updateOrInsert` method accepts two arguments: a vector of conditions by which to find the record, and a vector of column and value pairs indicating the columns to be updated.
|
||||
|
||||
The `updateOrInsert` method will attempt to locate a matching database record using the first argument's column and value pairs. If the record exists, it will be updated with the values in the second argument. If the record can not be found, a new record will be inserted with the merged attributes of both arguments:
|
||||
|
||||
DB::table("users")
|
||||
->updateOrInsert(
|
||||
{{"email", "john@example.com"}, {"name", "John"}},
|
||||
{{"votes", 2}}
|
||||
);
|
||||
|
||||
### Increment & Decrement {#increment-and-decrement}
|
||||
|
||||
The query builder also provides convenient methods for incrementing or decrementing the value of a given column. Both of these methods accept at least one argument: the column to modify. A second argument may be provided to specify the amount by which the column should be incremented or decremented:
|
||||
|
||||
@@ -119,6 +119,10 @@ namespace Orm::Query
|
||||
/*! 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);
|
||||
|
||||
/*! Delete records from the database. */
|
||||
std::tuple<int, QSqlQuery> deleteRow();
|
||||
|
||||
@@ -222,6 +222,39 @@ Builder::update(const QVector<UpdateItem> &values)
|
||||
values)));
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
/*! Merge attributes and values for the updateOrInsert() method. */
|
||||
const auto mergeValuesForInsert = [](const QVector<WhereItem> &attributes,
|
||||
const QVector<UpdateItem> &values)
|
||||
{
|
||||
QVariantMap result;
|
||||
|
||||
for (const auto &attribute : attributes)
|
||||
/* Can not contain expression in the column name, throws
|
||||
the std::bad_variant_access exception. */
|
||||
result.insert(std::get<QString>(attribute.column), attribute.value);
|
||||
|
||||
for (const auto &value : values)
|
||||
result.insert(value.column, value.value);
|
||||
|
||||
return result;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
std::tuple<int, std::optional<QSqlQuery>>
|
||||
Builder::updateOrInsert(const QVector<WhereItem> &attributes,
|
||||
const QVector<UpdateItem> &values)
|
||||
{
|
||||
if (!where(attributes).exists())
|
||||
return {-1, insert(mergeValuesForInsert(attributes, values))};
|
||||
|
||||
if (values.isEmpty())
|
||||
return {0, std::nullopt};
|
||||
|
||||
return limit(1).update(values);
|
||||
}
|
||||
|
||||
std::tuple<int, QSqlQuery> Builder::deleteRow()
|
||||
{
|
||||
return remove();
|
||||
|
||||
@@ -44,6 +44,9 @@ private Q_SLOTS:
|
||||
void implode_EmptyResult() const;
|
||||
void implode_QualifiedColumnOrKey() const;
|
||||
|
||||
void updateOrInsert() const;
|
||||
void updateOrInsert_EmptyValues() const;
|
||||
|
||||
void count() const;
|
||||
void count_Distinct() const;
|
||||
void min_Aggregate() const;
|
||||
@@ -399,6 +402,108 @@ void tst_QueryBuilder::implode_QualifiedColumnOrKey() const
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QueryBuilder::updateOrInsert() const
|
||||
{
|
||||
QFETCH_GLOBAL(QString, connection);
|
||||
|
||||
// update
|
||||
{
|
||||
auto [affected, query] = createQuery(connection)->from("user_phones").
|
||||
updateOrInsert(
|
||||
{{"user_id", 3}, {"number", "905111999"}},
|
||||
{{"number", "905333999"}});
|
||||
|
||||
QVERIFY(query);
|
||||
QVERIFY(!query->isValid() && !query->isSelect() && query->isActive());
|
||||
QCOMPARE(affected, 1);
|
||||
|
||||
// validate
|
||||
auto count = createQuery(connection)->from("user_phones")
|
||||
.whereEq("user_id", 3)
|
||||
.count();
|
||||
QCOMPARE(count, 1);
|
||||
auto number = createQuery(connection)->from("user_phones")
|
||||
.whereEq("user_id", 3)
|
||||
.value("number");
|
||||
QCOMPARE(number, QString("905333999"));
|
||||
}
|
||||
|
||||
// remove
|
||||
{
|
||||
auto [affected, query] = createQuery(connection)->from("user_phones")
|
||||
.whereEq("user_id", 3)
|
||||
.remove();
|
||||
|
||||
QVERIFY(!query.isValid() && !query.isSelect() && query.isActive());
|
||||
QCOMPARE(affected, 1);
|
||||
|
||||
// validate
|
||||
QVERIFY(createQuery(connection)->from("user_phones")
|
||||
.whereEq("user_id", 3)
|
||||
.doesntExist());
|
||||
}
|
||||
|
||||
// insert (also restore db)
|
||||
{
|
||||
auto [affected, query] = createQuery(connection)->from("user_phones").
|
||||
updateOrInsert(
|
||||
{{"user_id", 3}, {"number", "905000000"}},
|
||||
{{"number", "905111999"}});
|
||||
|
||||
QVERIFY(query);
|
||||
QVERIFY(!query->isValid() && !query->isSelect() && query->isActive());
|
||||
QCOMPARE(affected, -1);
|
||||
|
||||
// validate
|
||||
auto count = createQuery(connection)->from("user_phones")
|
||||
.whereEq("user_id", 3)
|
||||
.count();
|
||||
QCOMPARE(count, 1);
|
||||
auto number = createQuery(connection)->from("user_phones")
|
||||
.whereEq("user_id", 3)
|
||||
.value("number");
|
||||
QCOMPARE(number, QString("905111999"));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QueryBuilder::updateOrInsert_EmptyValues() const
|
||||
{
|
||||
QFETCH_GLOBAL(QString, connection);
|
||||
|
||||
// validate
|
||||
auto count = createQuery(connection)->from("user_phones")
|
||||
.whereEq("user_id", 3)
|
||||
.count();
|
||||
QCOMPARE(count, 1);
|
||||
|
||||
// Get a current ID, it can not change
|
||||
auto expectedId = createQuery(connection)->from("user_phones")
|
||||
.whereEq("user_id", 3)
|
||||
.value(ID).value<quint64>();
|
||||
QVERIFY(expectedId >= 3);
|
||||
|
||||
// main operation
|
||||
auto [affected, query] = createQuery(connection)->from("user_phones").
|
||||
updateOrInsert(
|
||||
{{"user_id", 3}, {"number", "905111999"}},
|
||||
{});
|
||||
|
||||
QVERIFY(!query);
|
||||
QCOMPARE(affected, 0);
|
||||
|
||||
// validate
|
||||
count = createQuery(connection)->from("user_phones")
|
||||
.whereEq("user_id", 3)
|
||||
.count();
|
||||
QCOMPARE(count, 1);
|
||||
|
||||
// ID is still the same
|
||||
auto id = createQuery(connection)->from("user_phones")
|
||||
.whereEq("user_id", 3)
|
||||
.value(ID).value<quint64>();
|
||||
QCOMPARE(id, expectedId);
|
||||
}
|
||||
|
||||
void tst_QueryBuilder::count() const
|
||||
{
|
||||
QFETCH_GLOBAL(QString, connection);
|
||||
|
||||
Reference in New Issue
Block a user