mirror of
https://github.com/silverqx/TinyORM.git
synced 2026-04-30 05:29:57 -05:00
1756 lines
57 KiB
Plaintext
1756 lines
57 KiB
Plaintext
Increase/bump the release version:
|
||
----------------------------------
|
||
|
||
- increase in the following files
|
||
|
||
include/orm/version.hpp
|
||
tests/TinyUtils/src/version.hpp
|
||
tom/include/tom/version.hpp
|
||
|
||
- and also informational version in badges
|
||
|
||
README.md
|
||
|
||
|
||
Proxy methods that internally call the toBase() (applySoftDeletes):
|
||
-------------------------------------------------------------------
|
||
|
||
I have little misunderstood when the SoftDeletes constraint will be applied, I thought that it will be applied only
|
||
during the TinyBuilder::toBase(), that is not absolutely true because whether it will be applied is controlled by
|
||
the Model::newQueryXyz() methods. These Model::newQueryXyz() methods are called on various places, somehwhere
|
||
the newQuery() (applies SoftDeletes) is called and on another places the newModelQuery() (doesn't apply SoftDeletes) or newQueryWithoutRelationships() is called.
|
||
|
||
Anyway I leave this new structure of proxy methods that are divided by the toBase() or getQuery() internal method calls,
|
||
it makes sense.
|
||
|
||
Proxy methods on the TinyBuilder that need to apply SoftDeletes (call toBase() internally).
|
||
|
||
aggregate
|
||
average
|
||
avg
|
||
count
|
||
dd
|
||
decrement
|
||
doesntExist
|
||
doesntExistOr
|
||
dump
|
||
exists
|
||
existsOr
|
||
explain (currently not implemented)
|
||
implode
|
||
increment
|
||
max
|
||
min
|
||
pluck
|
||
remove
|
||
sum
|
||
toSql
|
||
update
|
||
upsert
|
||
|
||
These three getters are discutable, but I will apply SoftDeletes, it won't hurt anything:
|
||
from
|
||
getConnection
|
||
getGrammar
|
||
|
||
There is not need to apply SoftDeletes for the getBindings() as the BuildsSoftDeletes is not adding any new bindings:
|
||
getBindings
|
||
|
||
There is not need to apply SoftDeletes on insert methods:
|
||
insert
|
||
insertGetId
|
||
insertOrIgnore
|
||
insertUsing
|
||
|
||
There is not need to apply SoftDeletes on these:
|
||
raw
|
||
|
||
|
||
QDateTime and database date/time types:
|
||
---------------------------------------
|
||
|
||
The TinyORM handles the QDateTime's time zone correctly, it converts a time zone to the time zone
|
||
defined in the "qt_timezone" connection configuration option and then it converts a QDateTime
|
||
instance to a string using the Model::u_dateFormat,
|
||
look at the Model::setAttribute() -> Model::fromDateTime().
|
||
|
||
Also, look at the commit that has an extensive description:
|
||
QDateTime overhaul 🤯🤐🙃 (1ded27bb)
|
||
|
||
MySQL:
|
||
---
|
||
Qt QMYSQL driver:
|
||
It's not so simple, first, it ignores the QDateTime timezone, it simply takes
|
||
a DateTime value that was given during the QDateTime creation (ctor/fromString/...),
|
||
and exactly this value will be sent to the MySQL database.
|
||
|
||
datetime column type:
|
||
- will save exactly the same value that was sent to the database
|
||
|
||
timestamp column type:
|
||
- is another story, here is important the MySQL timezone session variable,
|
||
the MySQL server converts the DateTime value that was sent to the UTC timezone
|
||
for storage and this UTC value will be saved in the database, the same is true during
|
||
retrieval of this value, so it converts it from the UTC to a session timezone
|
||
|
||
Database:
|
||
MySQL database converts TIMESTAMP values from the current timezone to UTC
|
||
for storage, and back from UTC to the current timezone for retrieval.
|
||
This does not occur for other types such as DATETIME.
|
||
By default, the current timezone for each connection is the server's time.
|
||
The timezone can be set on a per-connection basis using the time_zone system
|
||
variable.
|
||
|
||
Summary:
|
||
Sent:
|
||
It IGNORES the QDateTime timezone!
|
||
MYSQL_TIME *myTime = toMySqlDate(val.toDate(), val.toTime(), val.userType());
|
||
And the toMySqlDate() function picks internally the year, month, hour, minute, ...
|
||
one by one, so it calls date.year(), date.month(), time.hour(), ...
|
||
Retrieval:
|
||
Returns in the local timezone.
|
||
- for prepared statements:
|
||
return QDateTime(date, time);
|
||
- for non-prepared statements:
|
||
return qDateTimeFromString(val); it internally calls
|
||
return QVariant(QDateTime::fromString(val, Qt::ISODate));
|
||
|
||
SQLite:
|
||
---
|
||
Qt QSQLITE driver:
|
||
It doesn't ignore the QDateTime timezone, it converts the QDateTime to a string using
|
||
.toString(Qt::ISODateWithMs) and this string value is sent to the database and
|
||
will be saved as a string column.
|
||
|
||
Database:
|
||
The SQLite database doesn't support DateTime-related types but has functions
|
||
for working with or converting DateTimes; they also allow to convert between
|
||
different timezones (using eg. strftime()).
|
||
|
||
Summary:
|
||
Sent:
|
||
It DOESN'T ignore the QDateTime timezone.
|
||
dateTime.toString(Qt::ISODateWithMs);
|
||
Retrieval:
|
||
It returns QVariant(QMetaType::QString).
|
||
- so it's up to a user how it will instantiate a QDateTime object from this
|
||
string value
|
||
|
||
PostgreSQL:
|
||
---
|
||
Qt QPSQL driver:
|
||
It's not so simple, it converts all QDateTime time zone to UTC and sends it to the database
|
||
in the ISO UTC format, for both for timestamps/datetime with or without time zone.
|
||
So the timestamps with time zone behave exactly like for the MySQL DB.
|
||
But timestamps without time zone don't, they are converted to the LOCAL DB server time zone
|
||
by DB before they are saved to DB storage.
|
||
|
||
Database:
|
||
The PostgreSQL server timezone can be set using the set TIME ZONE or set TIMEZONE TO
|
||
session variable or by the PGTZ environment variable, or of course using the main
|
||
configuration file and using the timezone configuration setting eg.
|
||
timezone = 'Europe/Bratislava'.
|
||
|
||
Summary:
|
||
Sent:
|
||
It DOESN'T ignore the QDateTime timezone.
|
||
// we force the value to be considered with a timezone information, and we force it to be UTC
|
||
// this is safe since postgresql stores only the UTC value and not the timezone offset (only used
|
||
// while parsing), so we have correct behavior in both case of with timezone and without tz
|
||
r = QStringLiteral("TIMESTAMP WITH TIME ZONE ") + QLatin1Char('\'') +
|
||
QLocale::c().toString(field.value().toDateTime().toUTC(), u"yyyy-MM-ddThh:mm:ss.zzz") +
|
||
QLatin1Char('Z') + QLatin1Char('\'');
|
||
Retrieval:
|
||
return QVariant(QDateTime::fromString(QString::fromLatin1(val),
|
||
Qt::ISODate).toLocalTime());
|
||
|
||
|
||
Handling NULL values:
|
||
---------------------
|
||
|
||
Also look qsqlresult.cpp -> QSqlResultPrivate::isVariantNull()
|
||
|
||
Also, look at the commit:
|
||
tests QDateTime null values (e8b3c3c5)
|
||
|
||
MySQL:
|
||
---
|
||
Summary:
|
||
Sent:
|
||
if (field.isNull())
|
||
r = QStringLiteral("NULL");
|
||
Retrieval:
|
||
return QVariant(f.type);
|
||
|
||
SQLite:
|
||
---
|
||
Summary:
|
||
Sent:
|
||
if (QSqlResultPrivate::isVariantNull(value))
|
||
res = sqlite3_bind_null(d->stmt, i + 1);
|
||
Retrieval:
|
||
case SQLITE_NULL:
|
||
values[i + idx] = QVariant(QMetaType::fromType<QString>());
|
||
|
||
PostgreSQL:
|
||
---
|
||
Summary:
|
||
Sent:
|
||
const auto nullStr = [](){ return QStringLiteral("NULL"); };
|
||
QString r;
|
||
if (field.isNull())
|
||
r = nullStr();
|
||
Retrieval:
|
||
if (PQgetisnull(d->result, currentRow, i))
|
||
return QVariant(type, nullptr);
|
||
|
||
|
||
Handling integer values:
|
||
------------------------
|
||
|
||
bool, smallint, bigint, ... are database types, _u suffix means unsigned.
|
||
Int, Short, LongLong, ... are QMetaType-s.
|
||
|
||
bool smallint smallint_u int int_u bigint bigint_u double decimal
|
||
MySQL Int Short UShort Int UInt LongLong ULongLong Double Double
|
||
PostgreSQL Bool Int - Int - - - Double Double
|
||
SQLite LongLong LongLong LongLong LongLong LongLong LongLong LongLong Double Double
|
||
|
||
Special rules:
|
||
-----
|
||
|
||
PostgreSQL:
|
||
---
|
||
- doesn't have unsigned integer numbers.
|
||
- returns Int for all types <Int
|
||
- bigint has special handling, it returns LongLong for positive numbers and ULongLong for negative,
|
||
it simply detects - character at the beginning
|
||
|
||
|
||
null values:
|
||
-----
|
||
|
||
In this case Int, Short, LongLong, ... are QMetaType-s that are passed to the QVariant ctor to
|
||
create a null QVariant eg. QVariant(QMetaType(QMetaType::Int)).
|
||
|
||
bool smallint smallint_u int int_u bigint bigint_u double decimal
|
||
MySQL Char Short UShort Int UInt LongLong ULongLong Double Double
|
||
PostgreSQL Bool Int - Int - LongLong LongLong Double Double
|
||
SQLite QString QString QString QString QString QString QString QString QString
|
||
|
||
Special rules:
|
||
-----
|
||
- QSQLITE driver - returns null QVariant(QMetaType(QMetaType::QString)) for all null values
|
||
- QMYSQL driver - returns null QVariant(QMetaType(QMetaType::Char)) for tinyint database types
|
||
- QPSQL driver - the logic for - and ULongLong is not applied for the null values, so it's
|
||
everytime LongLong
|
||
|
||
|
||
All places where is created some Model instance:
|
||
------------------------------------------------
|
||
|
||
The following list contains only a direct Model's constructor calls like "Derived model;",
|
||
these direct constructor calls have been replaced everywhere to Model::instance() related
|
||
methods to support the Default Attribute values.
|
||
All instance allocation are on the stack unless otherwise noted.
|
||
|
||
Model:
|
||
ctors
|
||
instance()
|
||
on()
|
||
query()
|
||
newTinyBuilder()
|
||
newFromBuilder()
|
||
newInstance()
|
||
|
||
HasAttributes:
|
||
getOriginal()
|
||
|
||
HasRelationships:
|
||
newRelatedInstance() - heap allocation
|
||
|
||
ModelProxies:
|
||
destroy()
|
||
|
||
BasePivot:
|
||
fromAttributes()
|
||
|
||
|
||
Todos to check in TinyOrm:
|
||
--------------------------
|
||
|
||
- QueryBuilder::insertGetId() allows insert with empty attributes, also Model::performInsert()
|
||
when incrementing == true, but all other insert methods don't, it's big inconsistency, unify it
|
||
|
||
|
||
Documentation TinyOrm Todos:
|
||
----------------------------
|
||
|
||
- how to refer NULL in docs, for now I leave it NULL
|
||
|
||
|
||
TODOs which look bad in code:
|
||
-----------------------------
|
||
|
||
- future add onDelete (and similar) callback feature
|
||
|
||
/*! Delete records from the database. */
|
||
void deleteModel()
|
||
{
|
||
// TODO future add onDelete (and similar) callback feature silverqx
|
||
// if (isset($this->onDelete)) {
|
||
// return call_user_func($this->onDelete, $this);
|
||
// }
|
||
|
||
return toBase().deleteRow();
|
||
}
|
||
|
||
- add c++20 compiler check, something like:
|
||
#ifdef __cplusplus
|
||
# if __cplusplus < 201103L && !defined(Q_CC_MSVC)
|
||
# error Qt requires a C++11 compiler and yours does not seem to be that.
|
||
# endif
|
||
#endif
|
||
|
||
- check this in cmake build:
|
||
#include(GenerateExportHeader)
|
||
#_test_compiler_hidden_visibility()
|
||
|
||
|
||
Todo categories:
|
||
----------------
|
||
|
||
Common:
|
||
|
||
- api different : different api than Laravel's Eloquent
|
||
- check : something to find out 🤔
|
||
- concept : add concept or constraint
|
||
- docs : document code or update markdown documentation
|
||
- desirable : feature which is extremely wanted
|
||
- dilemma : some sort of a fuckup
|
||
- duplicate : duplicate code
|
||
- feature : some feature to implement, perpend before feature described below
|
||
- future : task which has lower priority, because still much to do
|
||
- mistake : bad decision during prototyping 😭
|
||
- move : c++ move semantics
|
||
- mystery : don't know why that stuff is happening, find out what's up
|
||
- now : do it before commit
|
||
- next : next thing in the row to do after commit
|
||
- overflow : add check code, eg when size_t to int conversion
|
||
- perf : performance
|
||
- production : check before deploy to production
|
||
- regex : mark regex-es, try to avoid them
|
||
- reliability : make things more robust and reliable
|
||
- repeat : tasks that should I make from time to time
|
||
- security : self explaining
|
||
- study : don't know how something works, need to check up
|
||
- sync : synchronization in multi thread environment silverqx
|
||
- test : tasks in auto tests
|
||
- types : juggling with c++ types
|
||
|
||
Features related/to implement:
|
||
|
||
- aggregates : aggregate values like count, max, min, avg and sum
|
||
- castable : attributes casting
|
||
- default attributes : Default Attribute Values
|
||
- dilemma primarykey : different types for primary keys
|
||
- expression : DB::raw() support in the query builder
|
||
- events : event system
|
||
- ga : github actions
|
||
- guarded : related to the mass assignable feature
|
||
- json columns : JSON support
|
||
- logging : logging related
|
||
- migrations : database migrations related
|
||
- multidriver : task related to adding support for another drivers PostgreSQL, SQLite and SQL Server
|
||
- pivot : pivot table in the many-to-many relationship
|
||
- postgres : specific to PostgreSQL server
|
||
- qt6 : related to Qt6 upgrade or compatibility
|
||
- read/write connection : read/write connection
|
||
- relations : relations related 🤓
|
||
- savepoints : database savepoints
|
||
- scopes : query scopes
|
||
- seeders : seeders related
|
||
- table prefix : table prefix in the query grammar
|
||
- tom : tom migrations related
|
||
|
||
|
||
Versions info:
|
||
--------------
|
||
|
||
This is laravel/framework version, not laravel/laravel version:
|
||
|
||
- I have cloned repository at - E:\htdocs\laravel-src-master
|
||
- based on Laravel v8.26.1
|
||
- upgrade to Laravel v8.41.0 ( 15.5.2021, but I didn't merged/reflected new changes to TinyORM )
|
||
- upgrade to Laravel v8.80.0 ( 19.1.2021, upgrade from v8.41.0 )
|
||
- compare URLs (remove after merge):
|
||
- https://github.com/laravel/framework/compare/v8.26.1...v8.41.0
|
||
- https://github.com/laravel/framework/compare/v8.41.0...v8.80.0
|
||
|
||
- upgraded to Laravel 9
|
||
|
||
- close all expanded sections in github compare diff:
|
||
|
||
document.querySelectorAll('.btn-octicon.js-details-target[aria-expanded=true]').forEach((value) => value.click())
|
||
|
||
|
||
Maintenance:
|
||
------------
|
||
|
||
- from time to time try:
|
||
- compile without PCH
|
||
- compile with Qt6, I have still problem with clazy
|
||
|
||
|
||
GitHub Actions:
|
||
---------------
|
||
|
||
- Mass deletion of GitHub workflow runs
|
||
- authenticate using the gh if auth expired
|
||
- gh auth login -h github.com
|
||
|
||
- dwr silverqx/TinyORM
|
||
- <tab> or <shift-tab> to un/select
|
||
- <enter> to delete selected runs
|
||
- ~/.dotfiles/bin/dwr
|
||
|
||
- upgrade to the latest dwr
|
||
- https://qmacro.org/blog/posts/2021/03/26/mass-deletion-of-github-actions-workflow-runs/
|
||
- https://raw.githubusercontent.com/qmacro/dotfiles/230c6df494f239e9d1762794943847816e1b7c32/scripts/dwr
|
||
|
||
- Runner images: https://github.com/actions/runner-images/tree/main/images
|
||
|
||
- all actions with versions currently used by TinyORM:
|
||
|
||
actions/checkout@v3
|
||
Chocobo1/setup-ccache-action@v1
|
||
mxschmitt/action-tmate@v3
|
||
actions/cache@v2
|
||
KyleMayes/install-llvm-action@v1
|
||
egor-tensin/vs-shell@v2
|
||
msys2/setup-msys2@v2
|
||
jurplel/install-qt-action@v3
|
||
|
||
- periodical upgrades:
|
||
|
||
- analyzers-qtX.yml
|
||
- add-apt-repository Clang 14
|
||
- clang-cl.yml
|
||
- Install LLVM and Clang 15.0.6 (KyleMayes/install-llvm-action@v1)
|
||
- Qt 6.3.2 install base components (jurplel/install-qt-action@v3)
|
||
- QMYSQL install driver dlls (Qt 6.3.2) - needed to rebuild the QSQL QMYSQL driver
|
||
- linux-qtX.yml
|
||
- add-apt-repository Clang 15
|
||
- msvc-2022.yml
|
||
- Qt 6.3.2 install base components (jurplel/install-qt-action@v3)
|
||
- QMYSQL install driver dlls (Qt 6.3.2) - needed to rebuild the QSQL QMYSQL driver
|
||
|
||
- how to update the clazy-standalone (analyzers.yml):
|
||
- update the QtCreator to latest version, copy libexec/qtcreator/clang/ to some empty folder
|
||
- leave only the clazy-standalone executable in the bin/ and lib/ folder, and compress it:
|
||
tar cjvf ../clazy-standalone.tar.bz2 .
|
||
|
||
- then update this file in the https://github.com/silverqx/files project
|
||
- no need to udpate the URL in the GitHub URL_CLAZY_STANDALONE_LINUX_X64 secret,
|
||
it's still the same:
|
||
https://github.com/silverqx/files/raw/main/clazy-standalone.tar.bz2
|
||
|
||
- prepend to the env. variables:
|
||
|
||
- name: Print env
|
||
run: |
|
||
echo "LD_LIBRARY_PATH: $LD_LIBRARY_PATH"
|
||
echo "LIBRARY_PATH: $LIBRARY_PATH"
|
||
echo "PATH: $PATH"
|
||
|
||
- name: TinyORM prepend to the system $PATH
|
||
working-directory: ..
|
||
run: |
|
||
echo "LD_LIBRARY_PATH=$PWD${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH" >> $GITHUB_ENV
|
||
echo "LIBRARY_PATH=$GITHUB_WORKSPACE" >> $GITHUB_ENV
|
||
pwd >> $GITHUB_PATH
|
||
|
||
- name: Print env
|
||
working-directory: ..
|
||
run: |
|
||
pwd
|
||
echo "LD_LIBRARY_PATH: $LD_LIBRARY_PATH"
|
||
echo "LIBRARY_PATH: $LIBRARY_PATH"
|
||
echo "PATH: $PATH"
|
||
|
||
- run: exit 1
|
||
|
||
- vcpkg debug:
|
||
|
||
- name: TinyORM cmake configure (msvc-cmake-debug)
|
||
run: >-
|
||
echo $env:VCPKG_ROOT
|
||
|
||
echo $env:VCPKG_DEFAULT_TRIPLET
|
||
|
||
echo $env:VCPKG_MAX_CONCURRENCY
|
||
|
||
cmake
|
||
-S .
|
||
-B ../TinyORM-builds-cmake/build-msvc-cmake-debug
|
||
...
|
||
|
||
- ssh into runner:
|
||
|
||
- shortcuts:
|
||
ctrl-b ? - help (show commands)
|
||
ctrl-b c - new window
|
||
ctrl-b l - last window (switch to)
|
||
ctrl-b n - next window (switch to)
|
||
ctrl-b p - previous window (switch to)
|
||
ctrl-b 0-9 - switch to window
|
||
ctrl-b [ - enable scroll mode (eg. Up Arrow or PgDn), press q to quit the scroll mode
|
||
|
||
ctrl-b " - split pane (horizontally)
|
||
ctrl-b % - split pane (vertically)
|
||
ctrl-b z - resize pane (toggle maximize)
|
||
ctrl-b M-up - resize pane (up, down, left, right; M- is L-ALT)
|
||
ctrl-b up - select pane (up, down, left, right)
|
||
|
||
- run if failure or success:
|
||
|
||
- name: Setup tmate session
|
||
if: ${{ always() }}
|
||
uses: mxschmitt/action-tmate@v3
|
||
with:
|
||
limit-access-to-actor: true
|
||
- run: exit 1
|
||
|
||
- !!! with if: ${{ failure() }} condition !!!
|
||
--------------------
|
||
|
||
- name: Setup tmate session
|
||
if: ${{ failure() }}
|
||
uses: mxschmitt/action-tmate@v3
|
||
with:
|
||
limit-access-to-actor: true
|
||
- run: exit 1
|
||
|
||
- all TinyORM env. variables
|
||
|
||
env:
|
||
DB_MYSQL_CHARSET: ${{ secrets.DB_MYSQL_CHARSET }}
|
||
DB_MYSQL_COLLATION: ${{ secrets.DB_MYSQL_COLLATION }}
|
||
DB_MYSQL_DATABASE: ${{ secrets.DB_MYSQL_DATABASE }}
|
||
DB_MYSQL_HOST: ${{ secrets.DB_MYSQL_HOST_SSL }}
|
||
DB_MYSQL_PASSWORD: ${{ secrets.DB_MYSQL_PASSWORD }}
|
||
DB_MYSQL_SSL_CA: ${{ runner.workspace }}/../mysql/data/ca.pem
|
||
DB_MYSQL_SSL_CERT: ${{ runner.workspace }}/../mysql/data/client-cert.pem
|
||
DB_MYSQL_SSL_KEY: ${{ runner.workspace }}/../mysql/data/client-key.pem
|
||
DB_MYSQL_USERNAME: ${{ secrets.DB_MYSQL_USERNAME }}
|
||
DB_PGSQL_CHARSET: utf8
|
||
DB_PGSQL_DATABASE: ${{ secrets.DB_PGSQL_DATABASE }}
|
||
DB_PGSQL_HOST: ${{ secrets.DB_PGSQL_HOST }}
|
||
DB_PGSQL_PASSWORD: ${{ secrets.DB_PGSQL_PASSWORD }}
|
||
DB_PGSQL_USERNAME: ${{ secrets.DB_PGSQL_USERNAME }}
|
||
DB_SQLITE_DATABASE: ${{ runner.temp }}/${{ secrets.DB_SQLITE_DATABASE }}
|
||
TOM_TESTDATA_ENV: testing
|
||
|
||
- print all contexts:
|
||
|
||
- name: Dump GitHub context
|
||
env:
|
||
GITHUB_CONTEXT: ${{ toJSON(github) }}
|
||
run: echo "$GITHUB_CONTEXT"
|
||
- name: Dump job context
|
||
env:
|
||
JOB_CONTEXT: ${{ toJSON(job) }}
|
||
run: echo "$JOB_CONTEXT"
|
||
- name: Dump steps context
|
||
env:
|
||
STEPS_CONTEXT: ${{ toJSON(steps) }}
|
||
run: echo "$STEPS_CONTEXT"
|
||
- name: Dump runner context
|
||
env:
|
||
RUNNER_CONTEXT: ${{ toJSON(runner) }}
|
||
run: echo "$RUNNER_CONTEXT"
|
||
- name: Dump strategy context
|
||
env:
|
||
STRATEGY_CONTEXT: ${{ toJSON(strategy) }}
|
||
run: echo "$STRATEGY_CONTEXT"
|
||
- name: Dump matrix context
|
||
env:
|
||
MATRIX_CONTEXT: ${{ toJSON(matrix) }}
|
||
run: echo "$MATRIX_CONTEXT"
|
||
- name: Dump env context
|
||
env:
|
||
ENV_CONTEXT: ${{ toJSON(env) }}
|
||
run: echo "$ENV_CONTEXT"
|
||
- run: exit 1
|
||
|
||
- query caches using the gh api
|
||
|
||
# Print all caches usage summary
|
||
gh api -H "Accept: application/vnd.github+json" /repos/silverqx/TinyORM/actions/cache/usage
|
||
# List caches
|
||
gh api -H "Accept: application/vnd.github+json" /repos/silverqx/TinyORM/actions/caches
|
||
# Delete all caches for TinyORM repo
|
||
gh api --method DELETE -H "Accept: application/vnd.github+json" /repos/silverqx/TinyORM/actions/caches
|
||
# Delete a cache by ID for TinyORM repo
|
||
gh api --method DELETE -H "Accept: application/vnd.github+json" /repos/silverqx/TinyORM/actions/caches/CACHE_ID
|
||
|
||
|
||
- ternary operator alternative:
|
||
|
||
${{ matrix.compiler.key == 'gcc' && 1 || 2 }}
|
||
|
||
- use quotes for strings:
|
||
|
||
${{ matrix.compiler.key == 'gcc' && '400M' || '250M' }}
|
||
|
||
|
||
- ccache initial build sizes:
|
||
|
||
First value is with a default compression and second uncompressed (ccache --show-compression)
|
||
|
||
with PCH:
|
||
|
||
linux-gcc-qt64 - 780MB (5.0GB)
|
||
linux-clang-qt63 - 440MB (1.3GB)
|
||
linux-gcc-qt5 - 770MB (4.5GB)
|
||
|
||
w/o PCH:
|
||
|
||
clang-cl-qt64 - 135MB (880MB)
|
||
msvc2022-qt64 - 190MB (1.5GB)
|
||
msvc2019-qt5 - 175MB (1.5GB)
|
||
linux-gcc-qt64 - 165MB (700MB)
|
||
linux-clang-qt63 - 100MB (680MB)
|
||
linux-gcc-qt5 - 115MB (665MB)
|
||
linux-clang-qt5 - 90MB (640MB)
|
||
|
||
MSYS2-clang-qt5 - 140MB (900MB)
|
||
MSYS2-clang-qt6 - 140MB (895MB)
|
||
MSYS2-gcc-qt5 - 180MB (940MB)
|
||
MSYS2-gcc-qt6 - 180MB (940MB)
|
||
|
||
|
||
- ccache debugging (Linux):
|
||
|
||
|
||
- name: Ccache enable debugging
|
||
id: ccache-debug
|
||
run: |
|
||
ccacheDebugDir0='${{ runner.temp }}/ccache_debug/run_0'
|
||
ccacheDebugDir1='${{ runner.temp }}/ccache_debug/run_1'
|
||
|
||
mkdir -p "$ccacheDebugDir0"
|
||
mkdir -p "$ccacheDebugDir1"
|
||
|
||
echo "CCACHE_DEBUG=1" >> $GITHUB_ENV
|
||
|
||
echo "TinyCcacheDebugDir0=$ccacheDebugDir0" >> $GITHUB_OUTPUT
|
||
echo "TinyCcacheDebugDir1=$ccacheDebugDir1" >> $GITHUB_OUTPUT
|
||
|
||
... do build here
|
||
|
||
- name: TinyORM cmake build ✨ (${{ matrix.compiler.key }}-cmake-debug) (ccache debug 0)
|
||
run: >-
|
||
export CCACHE_DEBUGDIR='${{ steps.ccache-debug.outputs.TinyCcacheDebugDir0 }}'
|
||
|
||
cmake --build ../TinyORM-builds-cmake/build-${{ matrix.compiler.key }}-cmake-debug
|
||
--target all --parallel 2
|
||
|
||
- name: Ccache statistics
|
||
run: |
|
||
ccache --show-stats -vv
|
||
ccache --zero-stats
|
||
|
||
- name: Ccache upload debugging logs (ccache debug 0)
|
||
uses: actions/upload-artifact@v3
|
||
with:
|
||
name: ccache_debug_run_0
|
||
path: ${{ steps.ccache-debug.outputs.TinyCcacheDebugDir0 }}
|
||
if-no-files-found: error
|
||
|
||
- name: TinyORM cmake build ✨ (${{ matrix.compiler.key }}-cmake-debug) (ccache debug 1)
|
||
run: >-
|
||
cmake --build ../TinyORM-builds-cmake/build-${{ matrix.compiler.key }}-cmake-debug
|
||
--target clean
|
||
|
||
export CCACHE_DEBUGDIR='${{ steps.ccache-debug.outputs.TinyCcacheDebugDir1 }}'
|
||
|
||
cmake --build ../TinyORM-builds-cmake/build-${{ matrix.compiler.key }}-cmake-debug
|
||
--target all --parallel 2
|
||
|
||
- name: Ccache statistics (ccache debug 1)
|
||
run: |
|
||
ccache --show-stats -vv
|
||
ccache --zero-stats
|
||
|
||
- name: Ccache upload debugging logs (ccache debug 1)
|
||
uses: actions/upload-artifact@v3
|
||
with:
|
||
name: ccache_debug_run_1
|
||
path: ${{ steps.ccache-debug.outputs.TinyCcacheDebugDir1 }}
|
||
if-no-files-found: error
|
||
|
||
|
||
RegEx-s:
|
||
-------
|
||
|
||
- const data members:
|
||
(?<![\(\)])(const) +.* +\bm_.*\b( +.*)?;$
|
||
(?<![\(\)])(const) +.* +\bm_.*\b +=
|
||
(?<![\(\)])(const) +.* +\bm_.*\b +{
|
||
- const data member references:
|
||
(?<![\(\)])(const) +.* +&\bm_.*\b( +.*)?;$
|
||
- all exceptions:
|
||
throw (.*::)?\w+(E|_error)
|
||
|
||
|
||
Powershell commands:
|
||
--------------------
|
||
|
||
- export todos to csv:
|
||
Get-ChildItem -Path *.cpp,*.hpp -Recurse | sls -Pattern ' (TODO|NOTE|FIXME|BUG|WARNING|CUR|FEATURE|TEST|FUTURE) ' -CaseSensitive | % { $_.Line = $_.Line.Trim().TrimStart('// '); return $_; } | select Line,LineNumber,Path | Export-Csv todos.csv -Delimiter ';' -NoTypeInformation
|
||
|
||
- search in todos:
|
||
Get-ChildItem -Path *.cpp,*.hpp -Recurse | sls -Pattern ' (TODO|NOTE|FIXME|BUG|WARNING|CUR|FEATURE|TEST|FUTURE) ' -CaseSensitive | % { $_.Line = $_.Line.Trim().TrimStart('// '); return $_; } | where Line -Match 'pch' | select Line,LineNumber,Path | ft -AutoSize
|
||
|
||
- filter out executed queries:
|
||
Get-Content .\tmp.sql | sls -Pattern '^(Executed prepared query)' | Set-Content executed_queries.sql
|
||
|
||
- TinyOrmPlayground - run InvokeXTimes.ps1 on Linux:
|
||
stp && sq5
|
||
export LD_LIBRARY_PATH=../../../TinyOrm/TinyOrm-builds-qmake/build-TinyOrm-Desktop_Qt_5_15_2_GCC_64bit_ccache-Debug/src
|
||
pwsh -NoLogo -NoProfile -File InvokeXTimes.ps1 2 ../../../TinyOrmPlayground/TinyOrmPlayground-builds-qmake/build-TinyOrmPlayground-Desktop_Qt_5_15_2_GCC_64bit_ccache-Debug/TinyOrmPlayground
|
||
|
||
- TinyORM - run InvokeXTimes.ps1 on Linux:
|
||
stp && sq5 && cdtq && cd build-TinyOrm-Desktop_Qt_5_15_2_GCC_64bit_ccache-Debug
|
||
export LD_LIBRARY_PATH=./src:./tests/TinyUtils${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH
|
||
export PATH=$HOME/Code/c/TinyORM/tools:$PATH
|
||
InvokeXTimes.ps1
|
||
pwsh -NoLogo -NoProfile -File InvokeXTimes.ps1 100
|
||
|
||
|
||
Powershell Clang analyzers:
|
||
---------------------------
|
||
|
||
qa-lint-tinyorm-qt5.ps1 is tailor-made for TinyORM project.
|
||
|
||
qa-clang-tidy.ps1, qa-clazy-standalone.ps1, qa-clazy-standalone-st.ps1 are more general, they can be used with any project, "-st" script calls raw clazy-standalone.exe.
|
||
|
||
run-clang-tidy.ps1, run-clazy-standalone.ps1 are raw Powershell wrappers around python run-clang-tidy/run-clazy-standalone.py python scripts.
|
||
|
||
Invoke the run-clang-tidy.ps1 manually:
|
||
--
|
||
run-clang-tidy.ps1 -use-color -extra-arg-before='-Qunused-arguments' -j=10 -p='E:\c\qMedia\TinyOrm\TinyOrm-builds-cmake\build-lint-qt6_Debug' -checks='-*,readability-convert-member-functions-to-static' '(?:src|tests)[\\\/]+.+?[\\\/]+(?!mocs_)[\w_\-\+]+\.cpp$'
|
||
|
||
|
||
- test *.hpp count
|
||
|
||
gci -Force -Recurse -Include *.hpp `
|
||
-Path E:\c\qMedia\TinyOrm\TinyOrm\include,E:\c\qMedia\TinyOrm\TinyOrm\examples,e:\c\qMedia\TinyOrm\TinyOrm\tom,E:\c\qMedia\TinyOrm\TinyOrm\tests | `
|
||
select -ExpandProperty FullName > b.txt
|
||
|
||
- test *.cpp count
|
||
|
||
cat .\compile_commands.json | sls '"file":' > a.txt
|
||
|
||
And use regex-es from analyzers-qt5/6.yml
|
||
|
||
|
||
pwsh autocomeplete:
|
||
-------------------
|
||
For a fast lookup for debugging.
|
||
|
||
- tom command:
|
||
- argument:
|
||
tom complete --word="mi" --commandline="tom mi" --position=6
|
||
|
||
- option:
|
||
tom complete --word="--" --commandline="tom migrate --" --position=14
|
||
tom complete --word="--p" --commandline="tom migrate --p" --position=15
|
||
|
||
|
||
cmake build commands:
|
||
---------------------
|
||
|
||
vcvars64.ps1
|
||
cd E:\c\qMedia\TinyOrm\TinyOrm-builds-cmake\build-cmake\
|
||
cmake.exe -S E:/c/qMedia/TinyOrm/TinyOrm -B E:/c/qMedia/TinyOrm/TinyOrm-builds-cmake/build-cmake -GNinja `
|
||
-DCMAKE_BUILD_TYPE:STRING=Debug `
|
||
-DCMAKE_TOOLCHAIN_FILE:PATH=E:/c_libs/vcpkg/scripts/buildsystems/vcpkg.cmake
|
||
|
||
cmake --build . --target all
|
||
|
||
- generate Graphviz dependency image:
|
||
|
||
cmake.exe -S E:/c/qMedia/TinyOrm/TinyOrm -B E:/c/qMedia/TinyOrm/TinyOrm-builds-cmake/build-cmake -GNinja `
|
||
-DCMAKE_BUILD_TYPE:STRING=Debug `
|
||
-DCMAKE_TOOLCHAIN_FILE:PATH=E:/c_libs/vcpkg/scripts/buildsystems/vcpkg.cmake `
|
||
--graphviz=E:/c/qMedia/TinyOrm/TinyOrm-builds-cmake/build-cmake/graph/graph.dot; `
|
||
`
|
||
dot -Tpng -o .\graph\graph.png .\graph\graph.dot; `
|
||
.\graph\graph.png
|
||
|
||
- running ctest (use CTEST_OUTPUT_ON_FAILURE=1 env. or --output-on-failure, to see what failed)
|
||
|
||
E:\c\qMedia\TinyOrm\TinyOrm\tests\auto\utils\testdata\dotenv.ps1
|
||
$env:Path = "E:\c\qMedia\TinyOrm\TinyOrm-builds-cmake\build-TinyOrm-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug-cmake;E:\c\qMedia\TinyOrm\TinyOrm-builds-cmake\build-TinyOrm-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug-cmake\tests\auto\utils;" + $env:Path
|
||
ctest
|
||
ctest --progress
|
||
|
||
- some debug output:
|
||
|
||
cmake --trace-expand --trace-source=tests/auto/unit/orm/query/mysql_querybuilder/CMakeLists.txt -LA ..
|
||
cmake -LA .
|
||
|
||
- full build command, not needed, I leave it here as a shortcut:
|
||
|
||
cmake.exe -S E:/c/qMedia/TinyOrm/TinyOrm -B E:/c/qMedia/TinyOrm/TinyOrm-builds-cmake/build-cmake -GNinja `
|
||
"-DCMAKE_BUILD_TYPE:STRING=Debug" `
|
||
"-DCMAKE_PROJECT_INCLUDE_BEFORE:PATH=E:/Qt/Tools/QtCreator/share/qtcreator/package-manager/auto-setup.cmake" `
|
||
"-DQT_QMAKE_EXECUTABLE:STRING=E:/Qt/5.15.2/msvc2019_64/bin/qmake.exe" `
|
||
"-DCMAKE_PREFIX_PATH:STRING=E:/Qt/5.15.2/msvc2019_64" `
|
||
"-DCMAKE_C_COMPILER:STRING=C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.29.30037/bin/HostX64/x64/cl.exe" `
|
||
"-DCMAKE_CXX_COMPILER:STRING=C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.29.30037/bin/HostX64/x64/cl.exe" ` "-DCMAKE_TOOLCHAIN_FILE:PATH=E:/c_libs/vcpkg/scripts/buildsystems/vcpkg.cmake"
|
||
|
||
- put TinyOrm and TinyUtils libraries on the system path:
|
||
|
||
$env:Path = "E:\c\qMedia\TinyOrm\TinyOrm-builds-cmake\build-cmake;E:\c\qMedia\TinyOrm\TinyOrm-builds-cmake\build-cmake\tests\auto\utils;" + $env:Path
|
||
|
||
|
||
TinyORM docs github pages:
|
||
--------------------------
|
||
tags: docusaurus
|
||
---
|
||
|
||
- npm run clear
|
||
|
||
Clear a Docusaurus site's generated assets, caches, build artifacts.
|
||
We recommend running this command before reporting bugs, after upgrading versions, or anytime you have issues with your Docusaurus site.
|
||
|
||
- deploy:
|
||
|
||
.\dotenv.ps1
|
||
npm run deploy; echo "Algolia Rebuild"; sleep 30; .\algolia_rebuild.ps1
|
||
|
||
- local development:
|
||
|
||
npm start
|
||
npm start -- --no-open
|
||
|
||
- update Algolia index by DocSearch:
|
||
|
||
.\dotenv.ps1
|
||
.\algolia_rebuild.ps1
|
||
|
||
- upgrade Docusaurus npm:
|
||
|
||
npm install @docusaurus/core@latest @docusaurus/plugin-client-redirects@latest @docusaurus/plugin-ideal-image@latest @docusaurus/preset-classic@latest
|
||
|
||
- upgrade Docusaurus yarn:
|
||
|
||
yarn upgrade @docusaurus/core@latest @docusaurus/preset-classic@latest
|
||
|
||
|
||
CMake Guidelines:
|
||
-----------------
|
||
|
||
All are snake-case unless otherwise specified.
|
||
|
||
- variable names:
|
||
- global variables: tiny_
|
||
- local variables: preferred camelCase or snake-case
|
||
- function parameters: lowercase and optional tiny_ prefix
|
||
- cmake_parse_arguments: TINY_
|
||
- option variables: upper case without prefix
|
||
- cached variables: TINY_
|
||
- function names has the tiny_ prefix
|
||
- compile definitions prefix by project eg. TINYORM_, TINYUTILS_
|
||
|
||
|
||
Drop all PostgreSQL tables:
|
||
---------------------------
|
||
|
||
DROP TABLE IF EXISTS "tag_properties" CASCADE;
|
||
DROP TABLE IF EXISTS "tag_torrent" CASCADE;
|
||
DROP TABLE IF EXISTS "torrent_tags" CASCADE;
|
||
DROP TABLE IF EXISTS "roles" CASCADE;
|
||
DROP TABLE IF EXISTS "role_user" CASCADE;
|
||
DROP TABLE IF EXISTS "users" CASCADE;
|
||
DROP TABLE IF EXISTS "user_phones" CASCADE;
|
||
DROP TABLE IF EXISTS "settings" CASCADE;
|
||
DROP TABLE IF EXISTS "torrents" CASCADE;
|
||
DROP TABLE IF EXISTS "torrent_peers" CASCADE;
|
||
DROP TABLE IF EXISTS "torrent_previewable_files" CASCADE;
|
||
DROP TABLE IF EXISTS "torrent_previewable_file_properties" CASCADE;
|
||
DROP TABLE IF EXISTS "file_property_properties" CASCADE;
|
||
DROP TABLE IF EXISTS "migrations" CASCADE;
|
||
DROP TABLE IF EXISTS "migrations_example" CASCADE;
|
||
DROP TABLE IF EXISTS "migrations_unit_testing" CASCADE;
|
||
|
||
|
||
UBSan:
|
||
------
|
||
|
||
Has to be invoked on Linux clang and errors are detected at runtime.
|
||
|
||
This enable the undefined group:
|
||
|
||
QMAKE_CXXFLAGS += -O1 -fsanitize=undefined
|
||
QMAKE_LFLAGS += -fsanitize=undefined
|
||
|
||
This with the above one! enables all the possible sanitizers:
|
||
|
||
QMAKE_CXXFLAGS += -O1 -fsanitize=nullability -fsanitize=float-divide-by-zero -fsanitize=unsigned-integer-overflow -fsanitize=implicit-conversion -fsanitize=local-bounds
|
||
QMAKE_LFLAGS += -fsanitize=nullability -fsanitize=float-divide-by-zero -fsanitize=unsigned-integer-overflow -fsanitize=implicit-conversion -fsanitize=local-bounds
|
||
|
||
Set in the QtCreator env. before execution, it suspends some errors detected in the Qt library:
|
||
|
||
UBSAN_OPTIONS=suppressions=/home/silverqx/Code/c/TinyOrm/TinyORM/tools/configs/UBSan.supp:print_stacktrace=0
|
||
|
||
|
||
Valgrind, callgrind, and KCachegrind:
|
||
-------------------------------------
|
||
|
||
This only works on the Linux, I have setup Gentoo for this.
|
||
|
||
- QtCreator debug view (ctrl+4) and open/switch to the Callgrind view (in the bottom view)
|
||
- simply start and open the resut in the KCachegrind
|
||
- described here - https://doc.qt.io/qtcreator/creator-cache-profiler.html
|
||
|
||
I have enabled debug symbols and source files for the glibc, so I can see everything nicely:
|
||
- described here - https://wiki.gentoo.org/wiki/Debugging
|
||
|
||
KCachegrind legend:
|
||
- Self Cost - function itself ("Self" is sometimes also referred to as "Exclusive" costs)
|
||
- Inclusive Cost - the cost including all called functions (Incl. in UI)
|
||
- described in the F1 KCachegrind help - Chapter 5. Questions and Answers
|
||
- sidebar Flat Profile next to the Search is the grouping dropdown by ELF is useful 🔥
|
||
- select function and in the bottom view select the "Call Graph" or "Callees" tab, they are most
|
||
useful 🔥
|
||
- you can double-click through the "Callees" tab down through most expensive functions and
|
||
track down what is causing high cost 🔥
|
||
- I have selected % Relative, Cycle Detection, Shorten Templates, and Cycle Estimation
|
||
in the top toolbar
|
||
- Instruction Fetch vs Cycle Estimation
|
||
- Instruction Fetch looks like the Instruction Read Access and it means how much assembler
|
||
instructions a selected function coasted
|
||
- I don't exactly understand it
|
||
- described here https://stackoverflow.com/questions/38311201/kcachegrind-cycle-estimation
|
||
- also these two video were useful:
|
||
- https://www.youtube.com/watch?v=h-0HpCblt3A&ab_channel=DerickRethans
|
||
- https://www.youtube.com/watch?v=iH-hDOuQfcY&ab_channel=DerickRethans
|
||
|
||
|
||
inline constants:
|
||
-----------------
|
||
|
||
- for static archive build only inline constants are allowed, this is the default for all scenarios, any ify
|
||
|
||
- cmake
|
||
- normal behavior is that user can switch between inline and extern constants using the INLINE_CONSTANTS cmake option
|
||
- default is to provide/show this INLINE_CONSTANTS cmake option with the default value OFF, so extern constants are enabled by default
|
||
- MinGW clang shared build crashes with inline constants
|
||
- so don't show INLINE_CONSTANTS cmake option and value is NOT DEFINED in this case so the default will be used and it's extern constants
|
||
- MinGW clang static build is not supported, problem with inline constants :/
|
||
- this is different than qmake build, it compiles (qmake has problem with duplicit symbols) but it crashes
|
||
- so throw cmake message(FATAL_ERROR) with a nice message in TinyHelpers tiny_check_unsupported_build()
|
||
- clang-cl shared build crashes with extern constants, so force to inline constants 😕🤔
|
||
- don't show INLINE_CONSTANTS cmake option and provide the default value ON using feature_option_dependent(INLINE_CONSTANTS) depends option, so inline constants is the default
|
||
- so inline constants are only one option with clang-cl
|
||
|
||
- qmake
|
||
- normal behavior is that user can switch between inline and extern constants using the inline_constants/extern_constants qmake CONFIG option
|
||
- default is when no qmake CONFIG option is set and in this case extern constants (extern_constants CONFIG option) is set/enabled
|
||
- MinGW clang shared build crashes with inline constants
|
||
- when user set inline_constants then qmake error() is thrown with nice message
|
||
- so the default is extern constants, if no CONFIG option was set or user can set the extern_constants qmake CONFIG option manually
|
||
- MinGW clang static build is not supported, contains a problem with duplicit symbols, this build type is disabled
|
||
- qmake error() is thrown with a nice message
|
||
- cmake compiles ok but crashes in this scenario
|
||
- clang-cl shared build crashes with extern constants, so force to inline constants 😕🤔
|
||
- when is shared build then inline_constants qmake CONFIG option is set (this is the default)
|
||
- when user set extern_constants then qmake error() is thrown with nice message
|
||
- so inline constants are only one option with clang-cl
|
||
|
||
The conclusion is that the funckin string constants 💥, it was bearable until I have added the clang-cl MSVC support.
|
||
|
||
|
||
constructor copy/move snippet:
|
||
------------------------------
|
||
|
||
Add code below to class you want to optimize and set breakpoints inside and you will see what cause what 😎:
|
||
|
||
Torrent(const Torrent &torrent)
|
||
: Model(torrent)
|
||
{
|
||
qDebug() << "Torrent copy ctor";
|
||
}
|
||
Torrent(Torrent &&torrent) noexcept
|
||
: Model(std::move(torrent))
|
||
{
|
||
qDebug() << "Torrent move ctor";
|
||
}
|
||
Torrent &operator=(const Torrent &torrent)
|
||
{
|
||
Model::operator=(torrent);
|
||
qDebug() << "Torrent copy assign";
|
||
return *this;
|
||
}
|
||
Torrent &operator=(Torrent &&torrent) noexcept
|
||
{
|
||
Model::operator=(std::move(torrent));
|
||
qDebug() << "Torrent move assign";
|
||
return *this;
|
||
}
|
||
|
||
|
||
conversions:
|
||
------------
|
||
|
||
Makes possible to assign QVector<AttributeItem> to the Model,
|
||
or implicitly converts a QVector<AttributeItem> to Model:
|
||
|
||
Model(const QVector<AttributeItem> &attributes);
|
||
Model(QVector<AttributeItem> &&attributes);
|
||
|
||
--
|
||
Allows initialize the Model with QVector<AttributeItem>:
|
||
|
||
Model(std::initializer_list<AttributeItem> attributes)
|
||
: Model(QVector<AttributeItem> {attributes.begin(), attributes.end()})
|
||
{}
|
||
|
||
--
|
||
Makes possible to assign the Model to the QVector<AttributeItem>,
|
||
or converts the Model to the QVector<AttributeItem>:
|
||
|
||
operator QVector<AttributeItem>() const;
|
||
|
||
|
||
Check copy/move/swap operations:
|
||
--------------------------------
|
||
|
||
{
|
||
using TypeToCheck = Orm::SqlQuery;
|
||
using ConstructibleFrom = QVariant;
|
||
|
||
qDebug() << std::is_trivial_v<TypeToCheck>;
|
||
|
||
qDebug() << "-- nothrow";
|
||
qDebug() << std::is_nothrow_default_constructible_v<TypeToCheck>;
|
||
qDebug() << std::is_nothrow_copy_constructible_v<TypeToCheck>;
|
||
qDebug() << std::is_nothrow_copy_assignable_v<TypeToCheck>;
|
||
qDebug() << std::is_nothrow_move_constructible_v<TypeToCheck>;
|
||
qDebug() << std::is_nothrow_move_assignable_v<TypeToCheck>;
|
||
qDebug() << std::is_nothrow_swappable_v<TypeToCheck>;
|
||
qDebug() << std::is_nothrow_destructible_v<TypeToCheck>;
|
||
|
||
qDebug() << "-- throw";
|
||
qDebug() << std::is_default_constructible_v<TypeToCheck>;
|
||
qDebug() << std::is_copy_constructible_v<TypeToCheck>;
|
||
qDebug() << std::is_copy_assignable_v<TypeToCheck>;
|
||
qDebug() << std::is_move_constructible_v<TypeToCheck>;
|
||
qDebug() << std::is_move_assignable_v<TypeToCheck>;
|
||
qDebug() << std::is_swappable_v<TypeToCheck>;
|
||
qDebug() << std::is_destructible_v<TypeToCheck>;
|
||
|
||
qDebug() << "-- trivially";
|
||
qDebug() << std::is_trivially_default_constructible_v<TypeToCheck>;
|
||
qDebug() << std::is_trivially_copyable_v<TypeToCheck>;
|
||
qDebug() << std::is_trivially_copy_constructible_v<TypeToCheck>;
|
||
qDebug() << std::is_trivially_copy_assignable_v<TypeToCheck>;
|
||
qDebug() << std::is_trivially_move_constructible_v<TypeToCheck>;
|
||
qDebug() << std::is_trivially_move_assignable_v<TypeToCheck>;
|
||
qDebug() << std::is_trivially_destructible_v<TypeToCheck>;
|
||
|
||
qDebug() << "-- nothrow constructible from";
|
||
qDebug() << std::is_nothrow_constructible_v<TypeToCheck, ConstructibleFrom>;
|
||
qDebug() << std::is_nothrow_constructible_v<TypeToCheck, const ConstructibleFrom &>;
|
||
qDebug() << std::is_nothrow_constructible_v<TypeToCheck, ConstructibleFrom &&>;
|
||
|
||
exit(0);
|
||
}
|
||
|
||
|
||
Ranges transform:
|
||
-----------------
|
||
|
||
const auto relationToWithItem = [](const auto &relation) -> WithItem
|
||
{
|
||
return WithItem {relation};
|
||
};
|
||
|
||
builder->with(relations | ranges::views::transform(relationToWithItem)
|
||
| ranges::to<QVector<WithItem>>());
|
||
|
||
|
||
DatabaseConnection config:
|
||
--------------------------
|
||
|
||
QHash<QString, QVariant> config {
|
||
// {"driver", "mysql"},
|
||
// {"url", qEnvironmentVariable("DATABASE_URL")},
|
||
// {"url", qEnvironmentVariable("MYSQL_DATABASE_URL")},
|
||
{"host", qEnvironmentVariable("DB_MYSQL_HOST", "127.0.0.1")},
|
||
{"port", qEnvironmentVariable("DB_MYSQL_PORT", "3306")},
|
||
{"database", qEnvironmentVariable("DB_MYSQL_DATABASE", "")},
|
||
{"username", qEnvironmentVariable("DB_MYSQL_USERNAME", "root")},
|
||
{"password", qEnvironmentVariable("DB_MYSQL_PASSWORD", "")},
|
||
// {"unix_socket", qEnvironmentVariable("DB_MYSQL_SOCKET", "")},
|
||
{"charset", qEnvironmentVariable("DB_MYSQL_CHARSET", "utf8mb4")},
|
||
{"collation", qEnvironmentVariable("DB_MYSQL_COLLATION", "utf8mb4_unicode_ci")},
|
||
// {"collation", qEnvironmentVariable("DB_MYSQL_COLLATION", "utf8mb4_0900_ai_ci")},
|
||
// {"timezone", "+00:00"},
|
||
// {"prefix", ""},
|
||
// {"prefix_indexes", true},
|
||
{"strict", true},
|
||
// {"engine", {}},
|
||
{"options", ""},
|
||
};
|
||
|
||
QHash<QString, QVariant> config {
|
||
{"driver", "QSQLITE"},
|
||
{"database", qEnvironmentVariable("DB_SQLITE_DATABASE", "")},
|
||
{"prefix", ""},
|
||
{"options", QVariantHash()},
|
||
{"foreign_key_constraints", qEnvironmentVariable("DB_SQLITE_FOREIGN_KEYS",
|
||
"true")},
|
||
{"check_database_exists", true},
|
||
};
|
||
|
||
|
||
QtCreator common CMake options:
|
||
-------------------------------
|
||
|
||
-G Ninja
|
||
-D CMAKE_BUILD_TYPE:STRING=Debug
|
||
-D BUILD_TESTS:BOOL=OFF
|
||
-D MATCH_EQUAL_EXPORTED_BUILDTREE:BOOL=ON
|
||
-D MYSQL_PING:BOOL=ON
|
||
-D ORM:BOOL=OFF
|
||
-D TOM:BOOL=ON
|
||
-D TOM_EXAMPLE:BOOL=ON
|
||
-D TOM_MIGRATIONS_DIR:PATH=database/migrations
|
||
-D VERBOSE_CONFIGURE:BOOL=OFF
|
||
-D CMAKE_VERBOSE_MAKEFILE:BOOL=OFF
|
||
-D CMAKE_DISABLE_PRECOMPILE_HEADERS:BOOL=OFF
|
||
-D CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=OFF
|
||
-D CMAKE_PROJECT_INCLUDE_BEFORE:PATH=%{IDE:ResourcePath}/package-manager/auto-setup.cmake
|
||
-D QT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable}
|
||
-D CMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX}
|
||
-D CMAKE_C_COMPILER:STRING=%{Compiler:Executable:C}
|
||
-D CMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx}
|
||
|
||
|
||
- for QtCreator:
|
||
|
||
-D CMAKE_CXX_COMPILER_LAUNCHER:FILEPATH=C:/Users/<username>/scoop/shims/ccache.exe
|
||
-D CMAKE_VERBOSE_MAKEFILE:BOOL=OFF
|
||
-D VERBOSE_CONFIGURE:BOOL=ON
|
||
-D BUILD_TESTS:BOOL=ON
|
||
-D MATCH_EQUAL_EXPORTED_BUILDTREE:BOOL=OFF
|
||
-D MYSQL_PING:BOOL=ON
|
||
-D ORM:BOOL=ON
|
||
-D TOM:BOOL=ON
|
||
-D TOM_EXAMPLE:BOOL=ON
|
||
-D TOM_MIGRATIONS_DIR:PATH=database/migrations
|
||
|
||
- MSYS2 ccache
|
||
|
||
-D CMAKE_CXX_COMPILER_LAUNCHER:FILEPATH=C:/msys64/ucrt64/bin/ccache.exe
|
||
|
||
|
||
DatabaseConnection debug code:
|
||
------------------------------
|
||
|
||
{
|
||
auto [ok, query] = select("select @@session.time_zone, @@global.time_zone");
|
||
while(query.next()) {
|
||
qDebug().nospace() << query.value(0).toString() << "\n"
|
||
<< query.value(1).toString();
|
||
}
|
||
}
|
||
|
||
{
|
||
auto [ok, query] = select("select @@session.character_set_client, @@session.character_set_connection, "
|
||
"@@session.character_set_results, @@session.collation_connection");
|
||
while(query.next()) {
|
||
qDebug().nospace() << query.value(0).toString() << "\n"
|
||
<< query.value(1).toString() << "\n"
|
||
<< query.value(2).toString() << "\n"
|
||
<< query.value(3).toString();
|
||
}
|
||
}
|
||
|
||
{
|
||
auto [ok, query] = select("select @@global.character_set_client, @@global.character_set_connection, "
|
||
"@@global.character_set_results, @@global.collation_connection");
|
||
while(query.next()) {
|
||
qDebug().nospace() << query.value(0).toString() << "\n"
|
||
<< query.value(1).toString() << "\n"
|
||
<< query.value(2).toString() << "\n"
|
||
<< query.value(3).toString();
|
||
}
|
||
}
|
||
|
||
{
|
||
auto [ok, query] = select("select @@global.sql_mode, @@session.sql_mode");
|
||
while(query.next()) {
|
||
qDebug().nospace() << query.value(0).toString() << "\n"
|
||
<< query.value(1).toString();
|
||
}
|
||
}
|
||
|
||
|
||
tmp notes:
|
||
----------
|
||
|
||
message(-------)
|
||
message(XXX config.pri)
|
||
message(PWD: $$PWD)
|
||
message(OUT_PWD: $$OUT_PWD)
|
||
message(_PRO_FILE_PWD_: $$_PRO_FILE_PWD_)
|
||
message(INCLUDEPATH: $$INCLUDEPATH)
|
||
message(-------)
|
||
|
||
|
||
tmp notes - Queryable columns:
|
||
------------------------------
|
||
|
||
template<SubQuery T>
|
||
struct Queryable
|
||
{
|
||
QString as;
|
||
// std::variant<std::function<void(Orm::QueryBuilder &)>> queryable;
|
||
T queryable;
|
||
};
|
||
|
||
Queryable(Orm::QueryBuilder &) -> Queryable<Orm::QueryBuilder &>;
|
||
|
||
|
||
/*! Set the columns to be selected. */
|
||
template<SubQuery T>
|
||
Builder &select(const QVector<Queryable<T>> &columns)
|
||
// Builder &select(const QVector<Queryable> &columns)
|
||
{
|
||
clearColumns();
|
||
|
||
for (const auto &q : columns)
|
||
selectSub(q.queryable, q.as);
|
||
|
||
return *this;
|
||
}
|
||
/*! Set the column to be selected. */
|
||
// template<SubQuery T>
|
||
// Builder &select(const Column &column);
|
||
// /*! Add new select columns to the query. */
|
||
// template<SubQuery T>
|
||
// Builder &addSelect(const QVector<Column> &columns);
|
||
// /*! Add a new select column to the query. */
|
||
// template<SubQuery T>
|
||
// Builder &addSelect(const Column &column);
|
||
|
||
|
||
/*! Makes "from" fetch from a subquery. */
|
||
// template<SubQuery T>
|
||
// Builder &whereSub(T &&query, const QVariant &value)
|
||
// {
|
||
// /* 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. */
|
||
// auto [queryString, bindings] = createSub(std::forward<T>(query));
|
||
|
||
// addBinding(bindings, BindingType::WHERE);
|
||
|
||
// return where(Expression(QStringLiteral("(%1)").arg(queryString)),
|
||
// QStringLiteral("="), value);
|
||
// }
|
||
|
||
|
||
Model copy ctor:
|
||
----------------
|
||
|
||
template<typename Derived, AllRelationsConcept ...AllRelations>
|
||
Model<Derived, AllRelations...>::Model(const Model &model)
|
||
: exists(model.exists)
|
||
, u_table(model.u_table)
|
||
, u_connection(model.u_connection)
|
||
, u_incrementing(model.u_incrementing)
|
||
, u_primaryKey(model.u_primaryKey)
|
||
, u_relations(model.u_relations)
|
||
, u_with(model.u_with)
|
||
, m_attributes(model.m_attributes)
|
||
, m_original(model.m_original)
|
||
, m_changes(model.m_changes)
|
||
, m_attributesHash(model.m_attributesHash)
|
||
, m_originalHash(model.m_originalHash)
|
||
, m_changesHash(model.m_changesHash)
|
||
, m_relations(model.m_relations)
|
||
, u_touches(model.u_touches)
|
||
, m_pivots(model.m_pivots)
|
||
, u_timestamps(model.u_timestamps)
|
||
{}
|
||
|
||
|
||
AssignmentList:
|
||
---------------
|
||
|
||
I want to save this pattern:
|
||
|
||
struct AssignmentListItem
|
||
{
|
||
QString column;
|
||
QVariant value;
|
||
};
|
||
|
||
class AssignmentList final : public QVector<AssignmentListItem>
|
||
{
|
||
// Inherit all the base class constructors, wow 😲✨
|
||
using QVector<AssignmentListItem>::QVector;
|
||
|
||
public:
|
||
AssignmentList(const QVariantHash &variantHash)
|
||
{
|
||
auto itHash = variantHash.constBegin();
|
||
while (itHash != variantHash.constEnd()) {
|
||
*this << AssignmentListItem({itHash.key(), itHash.value()});
|
||
++itHash;
|
||
}
|
||
}
|
||
};
|
||
|
||
|
||
Example of the std::hash<> specialization:
|
||
------------------------------------------
|
||
|
||
/*! The std::hash specialization for the CastItem. */
|
||
template<>
|
||
class std::hash<TINYORM_END_COMMON_NAMESPACE::Orm::Tiny::CastItem>
|
||
{
|
||
/*! Alias for the CastItem. */
|
||
using CastItem = TINYORM_END_COMMON_NAMESPACE::Orm::Tiny::CastItem;
|
||
/*! Alias for the CastType. */
|
||
using CastType = TINYORM_END_COMMON_NAMESPACE::Orm::Tiny::CastType;
|
||
/*! Alias for the helper utils. */
|
||
using Helpers = Orm::Utils::Helpers;
|
||
|
||
public:
|
||
/*! Generate hash for the given CastItem. */
|
||
inline std::size_t operator()(const CastItem &castItem) const noexcept
|
||
{
|
||
/*! CastType underlying type. */
|
||
using CastTypeUnderlying = std::underlying_type_t<CastType>;
|
||
|
||
std::size_t resultHash = 0;
|
||
|
||
const auto castType = static_cast<CastTypeUnderlying>(castItem.type());
|
||
|
||
Helpers::hashCombine<CastTypeUnderlying>(resultHash, castType);
|
||
Helpers::hashCombine<QString>(resultHash, castItem.modifier());
|
||
|
||
return resultHash;
|
||
}
|
||
};
|
||
|
||
|
||
EntityManager.hpp:
|
||
------------------
|
||
|
||
#ifndef ENTITYMANAGER_H
|
||
#define ENTITYMANAGER_H
|
||
|
||
#include "orm/databaseconnection.hpp"
|
||
#include "orm/repositoryfactory.hpp"
|
||
|
||
#ifdef TINYORM_COMMON_NAMESPACE
|
||
namespace TINYORM_COMMON_NAMESPACE
|
||
{
|
||
#endif
|
||
namespace Orm
|
||
{
|
||
|
||
/*! The EntityManager is the central access point to ORM functionality. */
|
||
class SHAREDLIB_EXPORT EntityManager final
|
||
{
|
||
Q_DISABLE_COPY(EntityManager)
|
||
|
||
public:
|
||
EntityManager(const QVariantHash &config);
|
||
EntityManager(DatabaseConnection &connection);
|
||
~EntityManager();
|
||
|
||
/*! Factory method to create EntityManager instances. */
|
||
static EntityManager create(const QVariantHash &config);
|
||
|
||
/*! Gets the repository for an entity class. */
|
||
template<typename Repository>
|
||
QSharedPointer<Repository> getRepository() const;
|
||
|
||
/*! Create a new QSqlQuery. */
|
||
QSqlQuery query() const;
|
||
/*! Get a new query builder instance. */
|
||
QSharedPointer<QueryBuilder> queryBuilder() const;
|
||
/*! Check database connection and show warnings when the state changed. */
|
||
bool pingDatabase();
|
||
/*! Start a new database transaction. */
|
||
bool transaction();
|
||
/*! Commit the active database transaction. */
|
||
bool commit();
|
||
/*! Rollback the active database transaction. */
|
||
bool rollback();
|
||
/*! Start a new named transaction savepoint. */
|
||
bool savepoint(const QString &id);
|
||
/*! Rollback to a named transaction savepoint. */
|
||
bool rollbackToSavepoint(const QString &id);
|
||
|
||
/*! Get underlying database connection. */
|
||
inline DatabaseConnection &connection() const
|
||
{ return m_db; }
|
||
|
||
protected:
|
||
/*! Factory method to create DatabaseConnection instances. */
|
||
static DatabaseConnection &
|
||
createConnection(const QVariantHash &config);
|
||
|
||
private:
|
||
/*! The database connection used by the EntityManager. */
|
||
DatabaseConnection &m_db;
|
||
/*! The repository factory used to create dynamic repositories. */
|
||
RepositoryFactory m_repositoryFactory;
|
||
};
|
||
|
||
template<typename Repository>
|
||
QSharedPointer<Repository> EntityManager::getRepository() const
|
||
{
|
||
return m_repositoryFactory.getRepository<Repository>();
|
||
}
|
||
|
||
} // namespace Orm
|
||
#ifdef TINYORM_COMMON_NAMESPACE
|
||
} // namespace TINYORM_COMMON_NAMESPACE
|
||
#endif
|
||
|
||
#endif // ENTITYMANAGER_H
|
||
|
||
|
||
EntityManager.cpp:
|
||
------------------
|
||
|
||
#include "orm/entitymanager.hpp"
|
||
|
||
#include <QtSql/QSqlQuery>
|
||
|
||
#ifdef TINYORM_COMMON_NAMESPACE
|
||
namespace TINYORM_COMMON_NAMESPACE
|
||
{
|
||
#endif
|
||
namespace Orm
|
||
{
|
||
|
||
/*!
|
||
\class EntityManager
|
||
\brief The EntityManager class manages repositories and a connection
|
||
to the database.
|
||
|
||
\ingroup database
|
||
\inmodule Export
|
||
|
||
EntityManager is the base class to work with the database, it creates
|
||
and manages repository classes by helping with the RepositoryFactory
|
||
class.
|
||
Creates the database connection which is represented by
|
||
DatabaseConnection class.
|
||
EntityManager should be used in controllers ( currently TorrentExporter
|
||
is like a controller class ), services, and repository classes to access
|
||
the database. There is no need to use the QSqlDatabase or the
|
||
DatabaseConnection classes directly.
|
||
EntityManager is also injected into a repository and a service
|
||
classes constructors.
|
||
The circular dependency problem is solved by including entitymanager.hpp
|
||
in the baserepository.hpp file.
|
||
*/
|
||
|
||
EntityManager::EntityManager(const QVariantHash &config)
|
||
: m_db(createConnection(config))
|
||
, m_repositoryFactory(*this)
|
||
{}
|
||
|
||
EntityManager::EntityManager(DatabaseConnection &connection)
|
||
: m_db(connection)
|
||
, m_repositoryFactory(*this)
|
||
{}
|
||
|
||
EntityManager::~EntityManager()
|
||
{
|
||
DatabaseConnection::freeInstance();
|
||
}
|
||
|
||
EntityManager EntityManager::create(const QVariantHash &config)
|
||
{
|
||
return EntityManager(createConnection(config));
|
||
}
|
||
|
||
QSqlQuery EntityManager::query() const
|
||
{
|
||
return m_db.query();
|
||
}
|
||
|
||
QSharedPointer<QueryBuilder> EntityManager::queryBuilder() const
|
||
{
|
||
return m_db.query();
|
||
}
|
||
|
||
bool EntityManager::pingDatabase()
|
||
{
|
||
return m_db.pingDatabase();
|
||
}
|
||
|
||
bool EntityManager::transaction()
|
||
{
|
||
return m_db.transaction();
|
||
}
|
||
|
||
bool EntityManager::commit()
|
||
{
|
||
return m_db.commit();
|
||
}
|
||
|
||
bool EntityManager::rollback()
|
||
{
|
||
return m_db.rollback();
|
||
}
|
||
|
||
bool EntityManager::savepoint(const QString &id)
|
||
{
|
||
return m_db.savepoint(id);
|
||
}
|
||
|
||
bool EntityManager::rollbackToSavepoint(const QString &id)
|
||
{
|
||
return m_db.rollbackToSavepoint(id);
|
||
}
|
||
|
||
DatabaseConnection &
|
||
EntityManager::createConnection(const QVariantHash &config)
|
||
{
|
||
return DatabaseConnection::create(config.find("database").value().toString(),
|
||
config.find("prefix").value().toString(),
|
||
config);
|
||
}
|
||
|
||
} // namespace Orm
|
||
#ifdef TINYORM_COMMON_NAMESPACE
|
||
} // namespace TINYORM_COMMON_NAMESPACE
|
||
#endif
|
||
|
||
|
||
Check whether first data member in BaseCommand is QString (unsuccessful):
|
||
---
|
||
|
||
struct CommandDefinition
|
||
{};
|
||
|
||
struct BaseCommand : CommandDefinition
|
||
{
|
||
QString name;
|
||
};
|
||
|
||
struct Idx1 : CommandDefinition
|
||
{
|
||
int i = 0;
|
||
QString name;
|
||
};
|
||
|
||
template<typename T>
|
||
concept IsQString = requires(T t)
|
||
{
|
||
// requires std::same_as<decltype (std::declval<T>().name), QString>;
|
||
{t.name} -> std::same_as<QString &>;
|
||
};
|
||
|
||
BaseCommand i {.name = "h i"};
|
||
Idx1 i1 {.i = 10, .name = "h i1"};
|
||
CommandDefinition &bi = i;
|
||
CommandDefinition &bi1 = i1;
|
||
|
||
if constexpr (IsQString<decltype (reinterpret_cast<BaseCommand &>(bi))>)
|
||
qDebug() << "y";
|
||
else
|
||
qDebug() << "n";
|
||
|
||
|
||
Test Column expressions code, just swap groupBy
|
||
-----
|
||
|
||
auto q1 = Torrent::find(1)->torrentFiles()->groupBy({"xyz", "abc"}).toSql();
|
||
qDebug() << q1;
|
||
auto q2 = Torrent::find(1)->torrentFiles()->groupBy("xyz").toSql();
|
||
qDebug() << q2;
|
||
auto q = Torrent::find(1)->torrentFiles()->groupBy("abc", "def").toSql();
|
||
qDebug() << q;
|
||
|
||
auto t1 = Torrent::find(1)->torrentFiles()->groupBy({DB::raw("xyz"), "abc"}).toSql();
|
||
qDebug() << t1;
|
||
auto t2 = Torrent::find(1)->torrentFiles()->groupBy(DB::raw("xyz")).toSql();
|
||
qDebug() << t2;
|
||
auto t = Torrent::find(1)->torrentFiles()->groupBy("abc", DB::raw("def")).toSql();
|
||
qDebug() << t;
|
||
|
||
QString s1("abc");
|
||
const QString s2("fgh");
|
||
auto q3 = Torrent::find(1)->torrentFiles()->groupBy(s1, s2).toSql();
|
||
qDebug() << q3;
|
||
auto q4 = Torrent::find(1)->torrentFiles()->groupBy(std::move(s1), s2).toSql();
|
||
qDebug() << q4;
|
||
|
||
const QString s3("jkl");
|
||
auto t3 = Torrent::find(1)->torrentFiles()->groupBy(s3, DB::raw(s2)).toSql();
|
||
qDebug() << t3;
|
||
auto t4 = Torrent::find(1)->torrentFiles()->groupBy(std::move(s3), DB::raw(s2)).toSql();
|
||
qDebug() << t4;
|
||
|
||
|
||
Performance measure timer:
|
||
--------------------------
|
||
|
||
#include <QElapsedTimer>
|
||
QElapsedTimer timer;
|
||
timer.start();
|
||
qDebug().noquote() << QStringLiteral("Elapsed in XX : %1ms").arg(timer.elapsed());
|
||
|
||
$startTime = microtime(true);
|
||
printf("Elapsed in %s : %sms\n", $connection,
|
||
number_format((microtime(true) - $startTime) * 1000, 2));
|
||
|
||
|
||
Fastly write to the file:
|
||
-------------------------
|
||
|
||
#include <fstream>
|
||
std::ofstream("E:/tmp/aa.txt", std::ios::out | std::ios::app) << "first\nsecond\n";
|
||
std::ofstream("E:/tmp/aa.txt", std::ios::out | std::ios::app) << "another line\n";
|
||
|
||
|
||
Invoke-Tests.ps1:
|
||
-----------------
|
||
|
||
- 100 times run
|
||
- 28. dec 2021
|
||
- Windows 10:
|
||
- Qt 5.15.2 ; msvc 16.11.8 x64
|
||
- debug build
|
||
All AutoTests Execution time : 792519ms
|
||
All AutoTests Average Execution time : 7925ms
|
||
|
||
- Qt 6.2.1 ; msvc 16.11.8 x64
|
||
- debug build
|
||
All AutoTests Execution time : 986531ms
|
||
All AutoTests Average Execution time : 9865ms
|
||
|
||
- Gentoo:
|
||
- Qt 5.15.2 ; GCC 11.2 x64 ccache
|
||
- debug build
|
||
All AutoTests Execution time : 519138ms
|
||
All AutoTests Average Execution time : 5191ms
|
||
- Qt 6.2.2 ; GCC 11.2 x64 ccache
|
||
- debug build
|
||
All AutoTests Execution time : 546585ms
|
||
All AutoTests Average Execution time : 5466ms
|
||
|
||
|
||
Compilation time and Memory usage:
|
||
----------------------------------
|
||
|
||
- 04. jun 2022 qmake ("CONFIG+=mysql_ping tom_example build_tests")
|
||
- Qt 6.2.4
|
||
MSVC2019 9.9GB 1:27
|
||
MSVC2022 8.0GB 1:25
|
||
clang-cl MSVC2022 5.5GB 1:35
|
||
|
||
|
||
bugs:
|
||
-----
|
||
|
||
- BUG std::unordered_map can not be instantiated with the incomplete value type, reproducible only on the Linux GCC/Clang, MSYS2 and msvc don't have any problem with the incomplete type ✨🚀
|
||
|
||
add this to the testforplay.cpp
|
||
|
||
#include <filesystem>
|
||
#include <iostream>
|
||
#include <typeindex>
|
||
#include <typeinfo>
|
||
|
||
#include <unordered_map>
|
||
|
||
namespace Models {
|
||
class Torrent;
|
||
}
|
||
|
||
struct Test1
|
||
{
|
||
std::unordered_map<QString, Models::Torrent> m_a {};
|
||
};
|
||
|
||
#include <range/v3/all.hpp>
|
||
|
||
#include <orm/db.hpp>
|
||
|
||
|
||
Unused code:
|
||
------------
|
||
|
||
Orm::Utils::Container:
|
||
|
||
- hpp
|
||
/*! Get a size of the greatest element in the container. */
|
||
template<QStringContainer T, typename SizeType = typename T::size_type>
|
||
static SizeType
|
||
maxElementSize(const T &container, typename T::size_type addToElement = 0);
|
||
|
||
template<QStringContainer T, typename SizeType>
|
||
SizeType
|
||
Container::maxElementSize(const T &container,
|
||
const typename T::size_type addToElement)
|
||
{
|
||
// Nothing to do
|
||
if (container.empty())
|
||
return 0;
|
||
|
||
SizeType result = 0;
|
||
|
||
for (const auto &element : container)
|
||
if (const auto elementSize = element.size();
|
||
elementSize > result
|
||
)
|
||
result = elementSize;
|
||
|
||
/* This is the reason for the addToElement argument, this algorithm returns 0,
|
||
if the result is 0. */
|
||
if (result == 0)
|
||
return 0;
|
||
|
||
return result + addToElement;
|
||
}
|
||
|
||
Orm::Utils::String:
|
||
|
||
- hpp
|
||
/*! Convert a string to kebab case. (kebab-case). */
|
||
inline static QString kebab(const QString &string);
|
||
/*! Get the singular form of an English word. */
|
||
static QString singular(const QString &string);
|
||
|
||
QString String::kebab(const QString &string)
|
||
{
|
||
return snake(string, Orm::Constants::DASH);
|
||
}
|
||
|
||
- cpp
|
||
QString String::singular(const QString &string)
|
||
{
|
||
if (!string.endsWith(QLatin1Char('s')))
|
||
return string;
|
||
|
||
return string.chopped(1);
|
||
}
|