mirror of
https://github.com/silverqx/TinyORM.git
synced 2026-02-04 09:19:22 -06:00
Fixed buggy behavior of QDateTime values during SELECT, INSERT, and UPDATE queries for all supported QtSql drivers. Behavior is fixed if querying the database using Orm::QueryBuilder or TinyBuilder/Model. Can't be fixed if using raw queries using the QSqlQuery because I don't have any control over this code. Every QtSql driver behaves differently in how it works with the QDateTime and datetime-related database types. It doesn't have only one problem, it has various kinds of problems, eg. it returns all QDateTime objects in the local time zone. All issues are summarized in NOTES.TXT under "QDateTime and database date/time types:" section. This buggy behavior can be fixed or corrected using a new "qt_timezone" database connection configuration option. It accepts the time zone value in various formats (QTimeZone, Qt::TimeSpec, int number as an offset from UTC, QString eg. +02:00, Europe/Prague or Orm::QtTimeZoneConfig). This "qt_timezone" value affects how the QueryBuilder and TinyBuilder send and receives datetime-related types to/from database. It should be set to the same time zone value as the "timezone" connection configuration option. During the INSERT and UPDATE statements, it CONVERTS QDateTime's time zone to the qt_timezone value before the statement is sent to the database. And during the SELECT statements it SETS QDateTime's time zone value after the values are obtained from the database. So if you set the "timezone" to UTC and "qt_timezone" to eg. Qt::UTC or QTimeZone::utc(), then QDateTime values will have set the correct time zone, in this case, the time zone will be UTC. For the SQLite database was also added the "return_qdatetime" connection configuration option which default value is true. By default, the QSQLITE driver returns datetime values as QString. The "return_qdatetime" controls this behavior and if is set to true then the QDateTime will be returned instead. TinyORM QueryBuilder returns the Orm::SqlQuery instead of QSqlQuery from methods for which the QDateTime's time zone should be corrected. That are eg. the select(), selectOne(), unprepared(), or chunk(), chunkById(), each(), eachById(), ... Orm::SqlQuery is a simple wrapper around the QSqlQuery whose responsibility is only to fix a QDateTime's time zone. Also unified the QDate behavior across all QtSql drivers. - added a new migration for the datetime table for testing QDateTime and datetime-related database types - added new Datetime model - added functional tests for testing datetime-related queries - testing QDateTime for all supported drivers - testing with different time zones, UTC, +02:00 - testing QDate for all supported drivers - added two new connection configuration options "qt_timezone" and "return_qdatetime" which is for the SQLite database only - added returnQDateTime()/setReturnQDateTime() getter/setter to the SQLiteConnection class - added getQtTimeZone()/setQtTimeZone()/isConvertingTimeZone() to the DatabaseConnection class - tests, fixed all QDateTime instances, changed time zone to UTC, so auto tests are now UTC, they force also MySQL and PostgreSQL server time zone session variable to UTC Others: - StringUtils moved from orm/tiny/utils/ to the orm/utils/ folder to the ::Orm::Utils namespace - enhanced all database connection constructors, used rvalue references - added delegated DatabaseConnection() constructor for SQLiteConnection() because of m_returnQDateTime
150 lines
7.0 KiB
C++
150 lines
7.0 KiB
C++
#include <orm/db.hpp>
|
|
|
|
#include <tom/application.hpp>
|
|
|
|
#include "migrations/2014_10_12_000000_create_posts_table.hpp"
|
|
#include "migrations/2014_10_12_100000_add_factor_column_to_posts_table.hpp"
|
|
#include "migrations/2014_10_12_200000_create_properties_table.hpp"
|
|
#include "migrations/2014_10_12_300000_create_phones_table.hpp"
|
|
|
|
#include "seeders/databaseseeder.hpp"
|
|
|
|
using Orm::DatabaseManager;
|
|
using Orm::DB;
|
|
|
|
using TomApplication = Tom::Application;
|
|
|
|
using namespace Migrations; // NOLINT(google-build-using-namespace)
|
|
using namespace Seeders; // NOLINT(google-build-using-namespace)
|
|
|
|
/*! Build the database manager instance and add a database connection. */
|
|
std::shared_ptr<DatabaseManager> setupManager();
|
|
|
|
/*! c++ main function. */
|
|
int main(int argc, char *argv[])
|
|
{
|
|
try {
|
|
// Ownership of the shared_ptr()
|
|
auto db = setupManager();
|
|
|
|
return TomApplication(argc, argv, std::move(db), "TOM_EXAMPLE_ENV",
|
|
QStringLiteral("migrations_example"))
|
|
/* Default migrations path for the make:migration command, the path
|
|
can be absolute or relative (to the pwd at runtime). */
|
|
// .migrationsPath("database/migrations")
|
|
// .migrationsPath(std::filesystem::current_path() / "database" / "migrations")
|
|
/* Migration classes can be named in two formats, StudlyCase without
|
|
the datetime prefix and snake_case with the datetime prefix.
|
|
If the StudlyCase name is used then the T_MIGRATION macro has to be
|
|
also used in the migration class. */
|
|
.migrations<CreatePostsTable,
|
|
// _2014_10_12_000000_create_posts_table,
|
|
AddFactorColumnToPostsTable,
|
|
CreatePropertiesTable,
|
|
CreatePhonesTable>()
|
|
/* Seeder classes, the DatabaseSeeder is the default/root seeder class,
|
|
it must always exist if the --class command-line argument is not
|
|
provided, or you can provide a custom name through the --class
|
|
argument.
|
|
The order of seeder classes doesn't matter, they will be called
|
|
in the order defined by the call<>() method inside the seeders
|
|
themselves. */
|
|
.seeders<DatabaseSeeder>()
|
|
// Fire it up 🔥🚀✨
|
|
.run();
|
|
|
|
} catch (const std::exception &e) {
|
|
|
|
TomApplication::logException(e);
|
|
}
|
|
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
std::shared_ptr<DatabaseManager> setupManager()
|
|
{
|
|
using namespace Orm::Constants; // NOLINT(google-build-using-namespace)
|
|
|
|
// Ownership of the shared_ptr()
|
|
return DB::create({
|
|
// MySQL connection
|
|
{QStringLiteral("tinyorm_tom_mysql"), { // shell:connection
|
|
{driver_, QMYSQL},
|
|
{host_, qEnvironmentVariable("DB_MYSQL_HOST", H127001)},
|
|
{port_, qEnvironmentVariable("DB_MYSQL_PORT", P3306)},
|
|
{database_, qEnvironmentVariable("DB_MYSQL_DATABASE", EMPTY)},
|
|
{username_, qEnvironmentVariable("DB_MYSQL_USERNAME", EMPTY)},
|
|
{password_, qEnvironmentVariable("DB_MYSQL_PASSWORD", EMPTY)},
|
|
{charset_, qEnvironmentVariable("DB_MYSQL_CHARSET", UTF8MB4)},
|
|
{collation_, qEnvironmentVariable("DB_MYSQL_COLLATION", UTF8MB40900aici)},
|
|
// SYSTEM - set the time zone to your local MySQL server time zone
|
|
{timezone_, TZ00},
|
|
// Specifies what time zone all QDateTime-s will have
|
|
{qt_timezone, QVariant::fromValue(Qt::UTC)},
|
|
{prefix_, EMPTY},
|
|
{prefix_indexes, true},
|
|
{strict_, true},
|
|
{isolation_level, QStringLiteral("REPEATABLE READ")},
|
|
{engine_, InnoDB},
|
|
{Version, {}}, // Autodetect
|
|
{options_, QVariantHash()},
|
|
}},
|
|
|
|
// PostgreSQL connection
|
|
{QStringLiteral("tinyorm_tom_postgres"), { // shell:connection
|
|
{driver_, QPSQL},
|
|
{host_, qEnvironmentVariable("DB_PGSQL_HOST", H127001)},
|
|
{port_, qEnvironmentVariable("DB_PGSQL_PORT", P5432)},
|
|
{database_, qEnvironmentVariable("DB_PGSQL_DATABASE", EMPTY)},
|
|
{schema_, qEnvironmentVariable("DB_PGSQL_SCHEMA", PUBLIC)},
|
|
{username_, qEnvironmentVariable("DB_PGSQL_USERNAME", postgres_)},
|
|
{password_, qEnvironmentVariable("DB_PGSQL_PASSWORD", EMPTY)},
|
|
{charset_, qEnvironmentVariable("DB_PGSQL_CHARSET", UTF8)},
|
|
// LOCAL/DEFAULT - set the time zone to your local PostgreSQL server time zone
|
|
{timezone_, UTC},
|
|
// Specifies what time zone all QDateTime-s will have
|
|
{qt_timezone, QVariant::fromValue(Qt::UTC)},
|
|
// Examples of qt_timezone
|
|
// {qt_timezone, QVariant::fromValue(QTimeZone("Europe/Bratislava"))},
|
|
// {qt_timezone, "Europe/Prague"}, // Will be converted to QTimeZone("Europe/Prague")
|
|
// {qt_timezone, QVariant::fromValue(QTimeZone("UTC+04"))},
|
|
// {qt_timezone, "-03:00"},
|
|
// {qt_timezone, 3600}, // Offset from UTC
|
|
// {qt_timezone, QVariant::fromValue(Qt::LocalTime)},
|
|
// {qt_timezone, {}}, // The same as Qt::LocalTime
|
|
{prefix_, EMPTY},
|
|
{prefix_indexes, true},
|
|
// ConnectionFactory provides a default value for this (for reference only)
|
|
// {dont_drop, QStringList {QStringLiteral("spatial_ref_sys")}},
|
|
{options_, QVariantHash()},
|
|
}},
|
|
|
|
// SQLite connection
|
|
{QStringLiteral("tinyorm_tom_sqlite"), { // shell:connection
|
|
{driver_, QSQLITE},
|
|
{database_, qEnvironmentVariable("DB_SQLITE_DATABASE", {})},
|
|
{foreign_key_constraints, true},
|
|
{check_database_exists, true},
|
|
// Specifies what time zone all QDateTime-s will have
|
|
{qt_timezone, QVariant::fromValue(Qt::UTC)},
|
|
// Return QDateTime with the correct time zone instead of QString
|
|
{return_qdatetime, true},
|
|
{prefix_, EMPTY},
|
|
}}
|
|
},
|
|
// MySQL as the default database connection
|
|
QStringLiteral("tinyorm_tom_mysql"));
|
|
}
|
|
|
|
/* Alternative syntax to instantiate migration classes. */
|
|
// return TomApplication(argc, argv, std::move(db), "TOM_EXAMPLE_ENV",
|
|
// QStringLiteral("migrations_table")
|
|
// {
|
|
// std::make_shared<CreatePostsTable>(),
|
|
// std::make_shared<AddFactorColumnToPostsTable>(),
|
|
// std::make_shared<CreatePropertiesTable>(),
|
|
// std::make_shared<CreatePhonesTable>(),
|
|
// })
|
|
// // Fire it up 🔥🚀✨
|
|
// .run();
|