Merge branch 'migrations' into develop

This commit is contained in:
silverqx
2022-04-21 16:31:53 +02:00
151 changed files with 8974 additions and 211 deletions

View File

@@ -53,8 +53,10 @@ set(minReqMsvcVersion 19.29)
set(minRecGCCVersion 10.2)
set(minRecClangVersion 12)
# Packages
set(minQtVersion 5.15.2)
set(minRangeV3Version 0.11.0)
set(minQtVersion 5.15.2)
set(minRangeV3Version 0.11.0)
# tabulate doesn't provide Package Version File
#set(minTabulateVersion 1.4.0)
# Make minimum toolchain version a requirement
tiny_toolchain_requirement(
@@ -157,11 +159,25 @@ option is disabled, then only the query builder without ORM is compiled."
DISABLED TINYORM_DISABLE_ORM
)
target_optional_compile_definitions(${TinyOrm_target}
PUBLIC
FEATURE NAME TOM
DEFAULT ON
DESCRIPTION "Controls the compilation of all tom related source code (command \
line interface)."
DISABLED TINYORM_DISABLE_TOM
)
# Depends on the TOM option so defined here
feature_option_dependent(TOM_EXAMPLE
"Build the tom command-line application example" OFF
"TOM" TOM_EXAMPLE-NOTFOUND
)
# TinyORM library header and source files
# ---
include(TinySources)
tiny_sources(${TinyOrm_target}_headers ${TinyOrm_target}_sources)
tinyorm_sources(${TinyOrm_target}_headers ${TinyOrm_target}_sources)
target_sources(${TinyOrm_target} PRIVATE
${${TinyOrm_target}_headers}
@@ -212,7 +228,6 @@ target_compile_definitions(${TinyOrm_target}
$<$<NOT:$<CONFIG:Debug>>:TINYORM_NO_DEBUG>
# Do not log queries
$<$<NOT:$<CONFIG:Debug>>:TINYORM_NO_DEBUG_SQL>
PRIVATE
# Debug build
$<$<CONFIG:Debug>:TINYORM_DEBUG>
# Log queries with a time measurement
@@ -237,10 +252,49 @@ else()
endif()
# Enable code needed by tests, eg. connection overriding in the Model
if(BUILD_TESTS)
if(BUILD_TESTS AND ORM)
target_compile_definitions(${TinyOrm_target} PUBLIC TINYORM_TESTS_CODE)
endif()
# TinyTom related header and source files
# ---
if(TOM)
tinytom_sources(${TomExample_target}_headers ${TomExample_target}_sources)
target_sources(${TinyOrm_target} PRIVATE
${${TomExample_target}_headers}
${${TomExample_target}_sources}
)
endif()
# TinyTom related specific configuration
# ---
if(TOM)
target_include_directories(${TinyOrm_target} PUBLIC
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/tom/include>"
)
endif()
# TinyTom related defines
# ---
if(TOM)
target_compile_definitions(${TinyOrm_target}
PUBLIC
# Release build
$<$<NOT:$<CONFIG:Debug>>:TINYTOM_NO_DEBUG>
# Debug build
$<$<CONFIG:Debug>:TINYTOM_DEBUG>
)
# Enable code needed by tests (modify the migrate:status command for tests need)
if(BUILD_TESTS)
target_compile_definitions(${TinyOrm_target} PUBLIC TINYTOM_TESTS_CODE)
endif()
endif()
# Windows resource and manifest files
# ---
@@ -285,6 +339,12 @@ if(MYSQL_PING)
target_link_libraries(${TinyOrm_target} PRIVATE MySQL::MySQL)
endif()
if(TOM)
# tabulate doesn't provide Package Version File
tiny_find_package(tabulate CONFIG REQUIRED)
target_link_libraries(${TinyOrm_target} PUBLIC tabulate::tabulate)
endif()
# Build auto tests
# ---
@@ -295,6 +355,13 @@ if(BUILD_TESTS)
add_subdirectory(tests)
endif()
# Build examples
# ---
if(TOM_EXAMPLE)
add_subdirectory(examples)
endif()
# Deployment
# ---
@@ -355,10 +422,20 @@ set_package_properties(range-v3
if(MYSQL_PING)
set_package_properties(MySQL
PROPERTIES
# URL and DESCRIPTION are already set by Find-module Package (FindMySQL.cmake)
TYPE REQUIRED
PURPOSE "Provides MySQL ping, enables MySqlConnection::pingDatabase()"
)
endif()
if(TOM)
set_package_properties(tabulate
PROPERTIES
URL "https://github.com/p-ranav/tabulate/"
DESCRIPTION "Table Maker for Modern C++"
TYPE REQUIRED
PURPOSE "Used by the tom in the migrate:status command"
)
endif()
if(VERBOSE_CONFIGURE)
if(NOT TINY_IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE STREQUAL "Debug")

View File

@@ -1,9 +1,16 @@
TEMPLATE = subdirs
SUBDIRS += src
SUBDIRS = src
tom_example {
SUBDIRS += examples
examples.depends = src
}
# Can be enabled by CONFIG += build_tests when the qmake.exe for the project is called
build_tests {
SUBDIRS += tests
tests.depends = src
!build_pass: message("Build TinyORM unit tests.")
}

View File

@@ -143,10 +143,12 @@ macro(tiny_init_tiny_variables_pre)
# a main package name
set(TinyOrm_ns TinyOrm)
set(TinyUtils_ns TinyUtils)
set(TomExample_ns tom)
# Target names
set(CommonConfig_target CommonConfig)
set(TinyOrm_target TinyOrm)
set(TinyUtils_target TinyUtils)
set(TomExample_target tom)
get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
set(TINY_IS_MULTI_CONFIG "${isMultiConfig}" CACHE INTERNAL

View File

@@ -1,7 +1,8 @@
# TinyORM library header and source files
# Create header and source files lists and return them
function(tiny_sources out_headers out_sources)
function(tinyorm_sources out_headers out_sources)
# ORM headers section
set(headers)
if(TINY_EXTERN_CONSTANTS)
@@ -131,10 +132,17 @@ function(tiny_sources out_headers out_sources)
tiny/types/connectionoverride.hpp
tiny/types/syncchanges.hpp
tiny/utils/attribute.hpp
)
endif()
# Headers used in both ORM and TOM
if(ORM OR TOM)
list(APPEND headers
tiny/utils/string.hpp
)
endif()
# ORM sources section
set(sources)
if(TINY_EXTERN_CONSTANTS)
@@ -203,6 +211,12 @@ function(tiny_sources out_headers out_sources)
tiny/tinytypes.cpp
tiny/types/syncchanges.cpp
tiny/utils/attribute.cpp
)
endif()
# Sources needed in both ORM and TOM
if(ORM OR TOM)
list(APPEND sources
tiny/utils/string.cpp
)
endif()
@@ -215,5 +229,91 @@ function(tiny_sources out_headers out_sources)
set(${out_headers} ${headers} PARENT_SCOPE)
set(${out_sources} ${sources} PARENT_SCOPE)
endfunction()
# TinyTom application header and source files
# Create header and source files lists and return them
function(tinytom_sources out_headers out_sources)
# Tom headers section
set(headers)
list(APPEND headers
application.hpp
commands/command.hpp
commands/database/wipecommand.hpp
commands/environmentcommand.hpp
commands/helpcommand.hpp
commands/inspirecommand.hpp
commands/listcommand.hpp
commands/make/migrationcommand.hpp
# commands/make/projectcommand.hpp
commands/make/stubs/migrationstubs.hpp
commands/make/stubs/projectstubs.hpp
commands/migrations/freshcommand.hpp
commands/migrations/installcommand.hpp
commands/migrations/migratecommand.hpp
commands/migrations/refreshcommand.hpp
commands/migrations/resetcommand.hpp
commands/migrations/rollbackcommand.hpp
commands/migrations/statuscommand.hpp
concerns/callscommands.hpp
concerns/confirmable.hpp
concerns/interactswithio.hpp
concerns/printsoptions.hpp
config.hpp
exceptions/invalidargumenterror.hpp
exceptions/invalidtemplateargumenterror.hpp
exceptions/logicerror.hpp
exceptions/runtimeerror.hpp
exceptions/tomerror.hpp
migration.hpp
migrationcreator.hpp
migrationrepository.hpp
migrator.hpp
terminal.hpp
tomtypes.hpp
version.hpp
)
# Tom sources section
set(sources)
list(APPEND sources
application.cpp
commands/command.cpp
commands/database/wipecommand.cpp
commands/environmentcommand.cpp
commands/helpcommand.cpp
commands/inspirecommand.cpp
commands/listcommand.cpp
commands/make/migrationcommand.cpp
# commands/make/projectcommand.cpp
commands/migrations/freshcommand.cpp
commands/migrations/installcommand.cpp
commands/migrations/migratecommand.cpp
commands/migrations/refreshcommand.cpp
commands/migrations/resetcommand.cpp
commands/migrations/rollbackcommand.cpp
commands/migrations/statuscommand.cpp
concerns/callscommands.cpp
concerns/confirmable.cpp
concerns/interactswithio.cpp
concerns/printsoptions.cpp
exceptions/tomlogicerror.cpp
exceptions/tomruntimeerror.cpp
migrationcreator.cpp
migrationrepository.cpp
migrator.cpp
terminal.cpp
)
list(SORT headers)
list(SORT sources)
list(TRANSFORM headers PREPEND "${CMAKE_SOURCE_DIR}/tom/include/tom/")
list(TRANSFORM sources PREPEND "${CMAKE_SOURCE_DIR}/tom/src/tom/")
set(${out_headers} ${headers} PARENT_SCOPE)
set(${out_sources} ${sources} PARENT_SCOPE)
endfunction()

View File

@@ -3,7 +3,7 @@ include(TinyResourceAndManifest)
# Configure passed auto test
function(tiny_configure_test name)
set(options INCLUDE_MODELS)
set(options INCLUDE_MIGRATIONS INCLUDE_MODELS)
cmake_parse_arguments(PARSE_ARGV 1 TINY "${options}" "" "")
if(DEFINED TINY_UNPARSED_ARGUMENTS)
@@ -54,6 +54,12 @@ ${TINY_UNPARSED_ARGUMENTS}")
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>"
)
if(TINY_INCLUDE_MIGRATIONS)
target_include_directories(${name} PRIVATE
"$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/tests/database>"
)
endif()
if(TINY_INCLUDE_MODELS)
target_include_directories(${name} PRIVATE
"$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/tests/models>"
@@ -62,8 +68,6 @@ ${TINY_UNPARSED_ARGUMENTS}")
target_link_libraries(${name}
PRIVATE
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Sql
Qt${QT_VERSION_MAJOR}::Test
${TinyOrm_ns}::${TinyUtils_target}
${TinyOrm_ns}::${TinyOrm_target}

View File

@@ -8,6 +8,7 @@
"supports": "!(uwp | arm | android | emscripten)",
"dependencies": [
"range-v3",
"tabulate",
"qt5-base",
{
"name": "vcpkg-cmake",

View File

@@ -21,6 +21,11 @@ win32-g++|win32-clang-g++ {
# Enable ccache wrapper
CONFIG *= tiny_ccache
# Includes
# tabulate
INCLUDEPATH += $$quote(C:/msys64/home/xyz/vcpkg/installed/x64-mingw-dynamic/include/)
QMAKE_CXXFLAGS += -isystem $$shell_quote(C:/msys64/home/xyz/vcpkg/installed/x64-mingw-dynamic/include/)
# Libraries
# MySQL C library
# Find with the pkg-config (preferred), shared build only
@@ -64,7 +69,7 @@ else:win32-msvc {
else:unix {
# Includes
# range-v3
QMAKE_CXXFLAGS += -isystem $$quote(/home/xyz/vcpkg/installed/x64-linux/include/)
QMAKE_CXXFLAGS += -isystem $$shell_quote(/home/xyz/vcpkg/installed/x64-linux/include/)
# Libraries
# MySQL C library

View File

@@ -617,7 +617,7 @@ TINY_TINYORM_BUILDS_DIR = $$quote($$TINY_MAIN_DIR/TinyOrm-builds-qmake)
include($$TINY_MAIN_DIR/TinyORM/qmake/TinyOrm.pri)
# TinyORM header files
QMAKE_CXXFLAGS += -isystem $$quote($$TINY_MAIN_DIR/TinyORM/include/)
QMAKE_CXXFLAGS += -isystem $$shell_quote($$TINY_MAIN_DIR/TinyORM/include/)
# TinyORM library path
LIBS += $$quote(-L$$TINY_TINYORM_BUILDS_DIR/build-TinyOrm-Desktop_Qt_5_15_2_GCC_64bit-Debug/src/debug/)
@@ -625,7 +625,7 @@ LIBS += -lTinyOrm
# vcpkg - range-v3
# ---
QMAKE_CXXFLAGS += -isystem $$quote(../../../../vcpkg/installed/x64-linux/include/)
QMAKE_CXXFLAGS += -isystem $$shell_quote(../../../../vcpkg/installed/x64-linux/include/)
```
</TabItem>

View File

@@ -456,7 +456,7 @@ TINY_TINYORM_BUILDS_DIR = $$quote($$TINY_MAIN_DIR/TinyOrm-builds-qmake)
include($$TINY_MAIN_DIR/TinyORM/qmake/TinyOrm.pri)
# TinyORM header files
QMAKE_CXXFLAGS += -isystem $$quote($$TINY_MAIN_DIR/TinyORM/include/)
QMAKE_CXXFLAGS += -isystem $$shell_quote($$TINY_MAIN_DIR/TinyORM/include/)
# TinyORM library path
LIBS += $$quote(-L$$TINY_TINYORM_BUILDS_DIR/build-TinyOrm-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug/src/debug/)
@@ -464,7 +464,7 @@ LIBS += -lTinyOrm
# vcpkg - range-v3
# ---
QMAKE_CXXFLAGS += -isystem $$quote(../../../../vcpkg/installed/x64-linux/include/)
QMAKE_CXXFLAGS += -isystem $$shell_quote(../../../../vcpkg/installed/x64-linux/include/)
```
:::tip

3
examples/CMakeLists.txt Normal file
View File

@@ -0,0 +1,3 @@
if(TOM_EXAMPLE)
add_subdirectory(tom)
endif()

7
examples/examples.pro Normal file
View File

@@ -0,0 +1,7 @@
TEMPLATE = subdirs
tom_example:!disable_tom {
SUBDIRS += tom
!build_pass: message("Build the tom example.")
}

View File

@@ -0,0 +1,74 @@
# Tom command-line application example
# ---
# Initialize Project Version
# ---
include(TinyHelpers)
tiny_read_version(TINY_VERSION
TINY_VERSION_MAJOR TINY_VERSION_MINOR TINY_VERSION_PATCH TINY_VERSION_TWEAK
VERSION_HEADER "${CMAKE_SOURCE_DIR}/tom/include/tom/version.hpp"
PREFIX TINYTOM
HEADER_FOR "${TomExample_ns}"
)
# Basic project
# ---
project(${TomExample_ns}
DESCRIPTION "Tom console for TinyORM"
HOMEPAGE_URL "https://silverqx.github.io/TinyORM/"
LANGUAGES CXX
VERSION ${TINY_VERSION}
)
# Tom command-line application
# ---
add_executable(${TomExample_target}
main.cpp
)
add_executable(${TomExample_ns}::${TomExample_target} ALIAS ${TomExample_target})
# Tom command-line application specific configuration
# ---
set_target_properties(${TomExample_target}
PROPERTIES
C_VISIBILITY_PRESET "hidden"
CXX_VISIBILITY_PRESET "hidden"
VISIBILITY_INLINES_HIDDEN YES
VERSION ${PROJECT_VERSION}
)
target_include_directories(${TomExample_target} PRIVATE
"$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/tests/database>"
)
# Tom command-line application defines
# ---
target_compile_definitions(${TomExample_target}
PRIVATE
PROJECT_TOMEXAMPLE
)
# Windows resource and manifest files
# ---
# Find icons, tom/version.hpp, and Windows manifest file for MinGW
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
tiny_set_rc_flags("-I \"${CMAKE_SOURCE_DIR}/tom/resources\"")
endif()
include(TinyResourceAndManifest)
tiny_resource_and_manifest(${TomExample_target}
OUTPUT_DIR "${TINY_BUILD_GENDIR}/tmp/"
RESOURCES_DIR "${CMAKE_SOURCE_DIR}/tom/resources"
)
# Resolve and link dependencies
# ---
# Unconditional dependencies
target_link_libraries(${TomExample_target} PRIVATE ${TinyOrm_ns}::${TinyOrm_target})

View File

@@ -0,0 +1,39 @@
# Migrations header files
# ---
# Tests' migrations as example migrations
include($$TINYORM_SOURCE_TREE/tests/database/migrations.pri)
# Or include yours migrations
#include(/home/xyz/your_project/database/migrations.pri)
# Dependencies include and library paths
# ---
# MinGW
win32-g++|win32-clang-g++ {
# Enable ccache wrapper
CONFIG *= tiny_ccache
# Includes
# tabulate
INCLUDEPATH += $$quote(C:/msys64/home/xyz/vcpkg/installed/x64-mingw-dynamic/include/)
QMAKE_CXXFLAGS += -isystem $$shell_quote(C:/msys64/home/xyz/vcpkg/installed/x64-mingw-dynamic/include/)
# Use faster linker
# CONFIG *= use_lld_linker does not work on MinGW
QMAKE_LFLAGS *= -fuse-ld=lld
}
else:win32-msvc {
# Includes
# range-v3 and tabulate
INCLUDEPATH += $$quote(E:/xyz/vcpkg/installed/x64-windows/include/)
}
else:unix {
# Includes
# range-v3 and tabulate
QMAKE_CXXFLAGS += -isystem $$shell_quote(/home/xyz/vcpkg/installed/x64-linux/include/)
# Use faster linkers
clang: CONFIG *= use_lld_linker
else: CONFIG *= use_gold_linker
}

99
examples/tom/main.cpp Normal file
View File

@@ -0,0 +1,99 @@
#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"
using Orm::Constants::H127001;
using Orm::Constants::P3306;
using Orm::Constants::QMYSQL;
using Orm::Constants::SYSTEM;
using Orm::Constants::UTF8MB4;
using Orm::Constants::charset_;
using Orm::Constants::collation_;
using Orm::Constants::database_;
using Orm::Constants::driver_;
using Orm::Constants::engine_;
using Orm::Constants::host_;
using Orm::Constants::InnoDB;
using Orm::Constants::isolation_level;
using Orm::Constants::options_;
using Orm::Constants::password_;
using Orm::Constants::port_;
using Orm::Constants::prefix_;
using Orm::Constants::prefix_indexes;
using Orm::Constants::strict_;
using Orm::Constants::timezone_;
using Orm::Constants::username_;
using Orm::DatabaseManager;
using Orm::DB;
using TomApplication = Tom::Application;
using namespace Migrations; // 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, db, "TOM_EXAMPLE_ENV")
.migrations<
_2014_10_12_000000_create_posts_table,
_2014_10_12_100000_add_factor_column_to_posts_table,
_2014_10_12_200000_create_properties_table,
_2014_10_12_300000_create_phones_table>()
// Fire it up 🔥🚀✨
.run();
} catch (const std::exception &e) {
TomApplication::logException(e);
}
return EXIT_FAILURE;
}
std::shared_ptr<DatabaseManager> setupManager()
{
// Ownership of the shared_ptr()
return DB::create({
{driver_, QMYSQL},
{host_, qEnvironmentVariable("DB_MYSQL_HOST", H127001)},
{port_, qEnvironmentVariable("DB_MYSQL_PORT", P3306)},
{database_, qEnvironmentVariable("DB_MYSQL_DATABASE", "")},
{username_, qEnvironmentVariable("DB_MYSQL_USERNAME", "")},
{password_, qEnvironmentVariable("DB_MYSQL_PASSWORD", "")},
{charset_, qEnvironmentVariable("DB_MYSQL_CHARSET", UTF8MB4)},
{collation_, qEnvironmentVariable("DB_MYSQL_COLLATION",
QStringLiteral("utf8mb4_0900_ai_ci"))},
{timezone_, SYSTEM},
{prefix_, ""},
{prefix_indexes, true},
{strict_, true},
{isolation_level, QStringLiteral("REPEATABLE READ")},
{engine_, InnoDB},
{options_, QVariantHash()},
},
QLatin1String("tinyorm_tom"));
}
/* Alternative syntax to instantiate migration classes. */
// return TomApplication(argc, argv, db, "TOM_EXAMPLE_ENV",
// {
// std::make_shared<_2014_10_12_000000_create_posts_table>(),
// std::make_shared<_2014_10_12_100000_add_factor_column_to_posts_table>(),
// std::make_shared<_2014_10_12_200000_create_properties_table>(),
// std::make_shared<_2014_10_12_300000_create_phones_table>(),
// })
// // Fire it up 🔥🚀✨
// .run();

53
examples/tom/tom.pro Normal file
View File

@@ -0,0 +1,53 @@
QT *= core sql
QT -= gui
TEMPLATE = app
TARGET = tom
# TinyTom example application specific configuration
# ---
CONFIG *= console
include($$TINYORM_SOURCE_TREE/qmake/tom.pri)
# TinyTom example application defines
# ---
DEFINES += PROJECT_TOMEXAMPLE
# TinyTom defines
# ---
# this define is not provided in the qmake/tom.pri
# Enable code needed by tests (modify the migrate:status command for tests need)
build_tests: \
DEFINES *= TINYTOM_TESTS_CODE
# TinyTom example application header and source files
# ---
SOURCES += $$PWD/main.cpp
# Deployment
# ---
win32-msvc:CONFIG(debug, debug|release) {
win32-msvc: target.path = C:/optx64/$${TARGET}
# else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
}
# User Configuration
# ---
exists(conf.pri): \
include(conf.pri)
#else:is_vcpkg_build: \
# include(../qmake/vcpkgconf.pri)
else: \
error( "'conf.pri' for 'tom' example project does not exist. See an example\
configuration in 'examples/tom/conf.pri.example' or call 'vcpkg install'\
in the project's root." )

View File

@@ -126,6 +126,9 @@ headersList += \
$$PWD/orm/tiny/types/connectionoverride.hpp \
$$PWD/orm/tiny/types/syncchanges.hpp \
$$PWD/orm/tiny/utils/attribute.hpp \
!disable_orm|!disable_tom: \
headersList += \
$$PWD/orm/tiny/utils/string.hpp \
HEADERS += $$sorted(headersList)

View File

@@ -179,7 +179,7 @@ namespace SchemaNs
/*! Get the query grammar used by the connection. */
inline QueryGrammar &getQueryGrammar();
/*! Get the schema grammar used by the connection. */
inline const SchemaGrammar &getSchemaGrammar() const;
const SchemaGrammar &getSchemaGrammar();
/*! Get a schema builder instance for the connection. */
virtual std::unique_ptr<SchemaBuilder> getSchemaBuilder();
/*! Get the query post processor used by the connection. */
@@ -401,11 +401,6 @@ namespace SchemaNs
return *m_queryGrammar;
}
const SchemaGrammar &DatabaseConnection::getSchemaGrammar() const
{
return *m_schemaGrammar;
}
const QueryProcessor &DatabaseConnection::getPostProcessor() const
{
return *m_postProcessor;

View File

@@ -5,8 +5,6 @@
#include "orm/macros/systemheader.hpp"
TINY_SYSTEM_HEADER
#include <stdexcept>
#include "orm/exceptions/logicerror.hpp"
TINYORM_BEGIN_COMMON_NAMESPACE
@@ -14,7 +12,7 @@ TINYORM_BEGIN_COMMON_NAMESPACE
namespace Orm::Exceptions
{
/*! Domain exception. */
/*! TinyORM Domain exception. */
class DomainError : public LogicError
{
/*! Inherit constructors. */

View File

@@ -5,8 +5,6 @@
#include "orm/macros/systemheader.hpp"
TINY_SYSTEM_HEADER
#include <stdexcept>
#include "orm/exceptions/logicerror.hpp"
TINYORM_BEGIN_COMMON_NAMESPACE
@@ -14,7 +12,7 @@ TINYORM_BEGIN_COMMON_NAMESPACE
namespace Orm::Exceptions
{
/*! Invalid argument exception. */
/*! TinyORM invalid argument exception. */
class InvalidArgumentError : public LogicError
{
/*! Inherit constructors. */

View File

@@ -12,7 +12,7 @@ TINYORM_BEGIN_COMMON_NAMESPACE
namespace Orm::Exceptions
{
/*! Invalid format exception. */
/*! TinyORM invalid format exception. */
class InvalidFormatError : public LogicError
{
/*! Inherit constructors. */

View File

@@ -12,7 +12,7 @@ TINYORM_BEGIN_COMMON_NAMESPACE
namespace Orm::Exceptions
{
/*! Invalid template argument exception. */
/*! TinyORM invalid template argument exception. */
class InvalidTemplateArgumentError : public InvalidArgumentError
{
/*! Inherit constructors. */

View File

@@ -10,7 +10,6 @@ TINY_SYSTEM_HEADER
#include <stdexcept>
#include "orm/exceptions/ormerror.hpp"
#include "orm/macros/commonnamespace.hpp"
#include "orm/macros/export.hpp"
TINYORM_BEGIN_COMMON_NAMESPACE
@@ -18,7 +17,7 @@ TINYORM_BEGIN_COMMON_NAMESPACE
namespace Orm::Exceptions
{
/*! Logic exception. */
/*! TinyORM Logic exception. */
class SHAREDLIB_EXPORT LogicError :
public std::logic_error,
public OrmError
@@ -28,16 +27,18 @@ namespace Orm::Exceptions
explicit LogicError(const char *message);
/*! QString constructor. */
explicit LogicError(const QString &message);
/*! std::string constructor. */
explicit LogicError(const std::string &message);
/*! Return exception message as a QString. */
const QString &message() const;
inline const QString &message() const noexcept;
protected:
/*! Exception message. */
QString m_message = what();
};
inline const QString &LogicError::message() const
const QString &LogicError::message() const noexcept
{
return m_message;
}

View File

@@ -16,7 +16,7 @@ TINYORM_BEGIN_COMMON_NAMESPACE
namespace Orm::Exceptions
{
/*! Database query exception. */
/*! TinyORM Database query exception. */
class SHAREDLIB_EXPORT QueryError : public SqlError
{
public:

View File

@@ -10,7 +10,6 @@ TINY_SYSTEM_HEADER
#include <stdexcept>
#include "orm/exceptions/ormerror.hpp"
#include "orm/macros/commonnamespace.hpp"
#include "orm/macros/export.hpp"
TINYORM_BEGIN_COMMON_NAMESPACE
@@ -18,7 +17,7 @@ TINYORM_BEGIN_COMMON_NAMESPACE
namespace Orm::Exceptions
{
/*! Runtime exception. */
/*! TinyORM Runtime exception. */
class SHAREDLIB_EXPORT RuntimeError :
public std::runtime_error,
public OrmError
@@ -28,16 +27,18 @@ namespace Orm::Exceptions
explicit RuntimeError(const char *message);
/*! QString constructor. */
explicit RuntimeError(const QString &message);
/*! std::string constructor. */
explicit RuntimeError(const std::string &message);
/*! Return exception message as a QString. */
const QString &message() const;
inline const QString &message() const noexcept;
protected:
/*! Exception message. */
QString m_message = what();
};
inline const QString &RuntimeError::message() const
const QString &RuntimeError::message() const noexcept
{
return m_message;
}

View File

@@ -14,7 +14,7 @@ TINYORM_BEGIN_COMMON_NAMESPACE
namespace Orm::Exceptions
{
/*! SqlError exception, wrapper for the QSqlError. */
/*! TinyORM SqlError exception, wrapper for the QSqlError. */
class SHAREDLIB_EXPORT SqlError : public RuntimeError
{
public:
@@ -24,7 +24,7 @@ namespace Orm::Exceptions
SqlError(const QString &message, const QSqlError &error);
/*! Get the original Qt SQL error. */
const QSqlError &getSqlError() const;
inline const QSqlError &getSqlError() const noexcept;
protected:
/*! Internal ctor for use from descendants to avoid an error message
@@ -38,6 +38,11 @@ namespace Orm::Exceptions
QSqlError m_sqlError;
};
const QSqlError &SqlError::getSqlError() const noexcept
{
return m_sqlError;
}
} // namespace Orm::Exceptions
TINYORM_END_COMMON_NAMESPACE

View File

@@ -12,7 +12,7 @@ TINYORM_BEGIN_COMMON_NAMESPACE
namespace Orm::Exceptions
{
/*! Sql transaction exception. */
/*! TinyORM Sql transaction exception. */
class SqlTransactionError : public SqlError
{
/*! Inherit constructors. */

View File

@@ -10,6 +10,7 @@ TINY_SYSTEM_HEADER
# define TINYORM_BEGIN_COMMON_NAMESPACE namespace TINYORM_COMMON_NAMESPACE {
# define TINYORM_END_COMMON_NAMESPACE }
#else
# define TINYORM_COMMON_NAMESPACE
# define TINYORM_BEGIN_COMMON_NAMESPACE
# define TINYORM_END_COMMON_NAMESPACE
#endif

View File

@@ -9,7 +9,7 @@ TINY_SYSTEM_HEADER
#include "orm/macros/commonnamespace.hpp"
#include "orm/macros/export.hpp"
// CUR check this on clean project silverqx
// CUR tom, check this on clean project silverqx
// Include the blueprint here so a user doesn't have to
#include "orm/schema/blueprint.hpp"

View File

@@ -16,17 +16,19 @@ TINY_SYSTEM_HEADER
TINYORM_BEGIN_COMMON_NAMESPACE
namespace Orm::Tiny
{
namespace TinyUtils = Orm::Tiny::Utils;
namespace Concerns
namespace Orm::Tiny::Concerns
{
/*! Model attributes. */
template<typename Derived, AllRelationsConcept ...AllRelations>
class HasAttributes
{
// CUR utils, use this using pattern also for all Orm::Utils silverqx
/*! Alias for the attribute utils. */
using AttributeUtils = Orm::Tiny::Utils::Attribute;
/*! Alias for the string utils. */
using StringUtils = Orm::Tiny::Utils::String;
public:
/*! Set a given attribute on the model. */
Derived &setAttribute(const QString &key, QVariant value);
@@ -258,7 +260,7 @@ namespace Concerns
const bool sync)
{
m_attributes.reserve(attributes.size());
m_attributes = TinyUtils::Attribute::removeDuplicitKeys(attributes);
m_attributes = AttributeUtils::removeDuplicitKeys(attributes);
// Build attributes hash
m_attributesHash.clear();
@@ -831,7 +833,7 @@ namespace Concerns
and format a Carbon object from this timestamp. This allows flexibility
when defining your date fields as they might be UNIX timestamps here. */
if (value.canConvert<QString>() &&
TinyUtils::String::isNumber(value.value<QString>())
StringUtils::isNumber(value.value<QString>())
)
// TODO switch ms accuracy? For the u_dateFormat too? silverqx
return QDateTime::fromSecsSinceEpoch(value.value<qint64>());
@@ -884,8 +886,7 @@ namespace Concerns
/* Static cast this to a child's instance type (CRTP) */
TINY_CRTP_MODEL_WITH_BASE_DEFINITIONS(HasAttributes)
} // namespace Concerns
} // namespace Orm::Tiny
} // namespace Orm::Tiny::Concerns
TINYORM_END_COMMON_NAMESPACE

View File

@@ -6,9 +6,9 @@
TINY_SYSTEM_HEADER
#ifdef __GNUG__
#include <map>
# include <map>
#else
#include <unordered_map>
# include <unordered_map>
#endif
#include <unordered_set>
@@ -955,7 +955,6 @@ namespace Concerns
QString
HasRelationships<Derived, AllRelations...>::guessBelongsToRelationInternal() const
{
// TODO reliability, also add Orm::Tiny::Utils::String::studly silverqx
auto relation = Orm::Utils::Type::classPureBasename<Related>();
relation[0] = relation[0].toLower();

View File

@@ -16,7 +16,7 @@ TINY_SYSTEM_HEADER
#include "orm/tiny/modelproxies.hpp"
#include "orm/tiny/tinybuilder.hpp"
#ifdef TINYORM_TESTS_CODE
#include "orm/tiny/types/connectionoverride.hpp"
# include "orm/tiny/types/connectionoverride.hpp"
#endif
TINYORM_BEGIN_COMMON_NAMESPACE
@@ -63,6 +63,10 @@ namespace Orm::Tiny
// Used by TinyBuilder::eagerLoadRelations()
friend TinyBuilder<Derived>;
/*! Alias for the attribute utils. */
using AttributeUtils = Orm::Tiny::Utils::Attribute;
/*! Alias for the string utils. */
using StringUtils = Orm::Tiny::Utils::String;
/*! Apply all the Model's template parameters to the passed T template
argument. */
template<template<typename ...> typename T>
@@ -893,8 +897,7 @@ namespace Orm::Tiny
if (table.isEmpty())
const_cast<QString &>(model().u_table) =
QStringLiteral("%1s").arg(
TinyUtils::String::toSnake(
Orm::Utils::Type::classPureBasename<Derived>()));
StringUtils::snake(Orm::Utils::Type::classPureBasename<Derived>()));
return table;
}
@@ -940,8 +943,7 @@ namespace Orm::Tiny
QString Model<Derived, AllRelations...>::getForeignKey() const
{
return QStringLiteral("%1_%2").arg(
TinyUtils::String::toSnake(
Orm::Utils::Type::classPureBasename<Derived>()),
StringUtils::snake(Orm::Utils::Type::classPureBasename<Derived>()),
getKeyName());
}
@@ -1076,7 +1078,7 @@ namespace Orm::Tiny
if (!dirty.isEmpty()) {
model().setKeysForSaveQuery(query).update(
TinyUtils::Attribute::convertVectorToUpdateItem(dirty));
AttributeUtils::convertVectorToUpdateItem(dirty));
this->syncChanges();
@@ -1327,3 +1329,22 @@ TINYORM_END_COMMON_NAMESPACE
// CUR model, add whereBelongsTo, whereRelation, orWhereRelation silverqx
// CUR schema, add tests for enum and set; and json and jsonb, storedAs / virtualAs silverqx
// CUR propagation, https://ben.balter.com/2017/11/10/twelve-tips-for-growing-communities-around-your-open-source-project/ silverqx
// CUR optimization, use Q_UNREACHABLE in all switch statements, of course where appropriate silverqx
// TODO vcpkg, solve how to build tom (when solving vcpkg builds again), currently I have hardly added tabulate to the vcpkg.json port and also manifest file; it will have to be conditional base of the TINYORM_DISABLE_TOM macro silverqx
// CUR tom docs, disable_tom and TINYORM_DISABLE_TOM to build.mdx, don't forget to add features and update dependencies (tabulate) in vcpkg.json silverqx
// CUR schema, add tests for enum and set; and json and jsonb, storedAs / virtualAs silverqx
// CUR compiler, enable /sdl on msvc https://docs.microsoft.com/en-us/cpp/build/reference/sdl-enable-additional-security-checks?view=msvc-170 silverqx
// CUR cmake, update max. policy to 3.23 silverqx
// CUR tom, add tabulate to comments where range-v3 is, all checked, only docs left silverqx
// CUR tom, verify -isystem $$shell_quote() updated in docs silverqx
// CUR tom, tom/conf.pri is used by who silverqx
// CUR cmake, add messages about Building tom example, tests and ORM silverqx
// CUR docs, remove target_link_libs() for transitive dependencies silverqx
// CUR tests, move version test outside of the orm/ folder silverqx
// CUR tom docs, write documentation silverqx
// CUR tom, build on mingw, linux, build without pch and all common tasks that should run from time to time silverqx
// CUR tom, update docs target_link_library() https://discourse.cmake.org/t/explicitly-link-against-public-interface-dependencies/5484/2 silverqx
// CUR tom, provide somehow a custom path to migrations for the tom example, so the tom example can be used like real migration app silverqx
// CUR tom, don't modify migrate:status command, rather extend it and add possibility to only call it through Application::runWithArguments() (this secure to not be able to call it from the cmd. line), do not show it in the list or help command output silverqx
// CUR tom, think about remove TINYTOM_NO/_DEBUG and TINYTOM_TESTS_CODE and use TINYORM_ defines instead silverqx
// BUG rc file © encoding silverqx

View File

@@ -20,6 +20,9 @@ namespace Orm::Tiny::Relations
template<typename PivotModel>
class BasePivot : public Model<PivotModel>, public IsPivotModel
{
/*! Alias for the string utils. */
using StringUtils = Orm::Tiny::Utils::String;
public:
friend Model<PivotModel>;
@@ -234,8 +237,8 @@ namespace Orm::Tiny::Relations
// Get singularizes snake-case table name
if (table.isEmpty())
return TinyUtils::String::singular(
TinyUtils::String::toSnake(
return StringUtils::singular(
StringUtils::snake(
Orm::Utils::Type::classPureBasename<PivotModel>()));
return table;

View File

@@ -25,8 +25,6 @@ TINYORM_BEGIN_COMMON_NAMESPACE
namespace Orm::Tiny::Relations
{
namespace TinyUtils = Orm::Tiny::Utils;
class Pivot;
/*! TinyORM's 'Pivot' class. */
@@ -46,6 +44,8 @@ namespace Orm::Tiny::Relations
{
Q_DISABLE_COPY(BelongsToMany)
/*! Alias for the attribute utils. */
using AttributeUtils = Orm::Tiny::Utils::Attribute;
/*! Model alias. */
template<typename Derived>
using BaseModel = Orm::Tiny::Model<Derived>;
@@ -953,7 +953,7 @@ namespace Orm::Tiny::Relations
return *instance;
return this->m_related->newInstance(
TinyUtils::Attribute::joinAttributesForFirstOr(
AttributeUtils::joinAttributesForFirstOr(
attributes, values, this->m_relatedKey));
}
@@ -969,7 +969,7 @@ namespace Orm::Tiny::Relations
return *instance;
// NOTE api different, Eloquent doen't use values argument silverqx
return create(TinyUtils::Attribute::joinAttributesForFirstOr(
return create(AttributeUtils::joinAttributesForFirstOr(
attributes, values, this->m_relatedKey),
pivotValues, touch);
}
@@ -1154,7 +1154,7 @@ namespace Orm::Tiny::Relations
we have inserted the records, we will touch the relationships if
necessary and the function will return. */
newPivotStatement()->insert(
TinyUtils::Attribute::convertVectorsToMaps(
AttributeUtils::convertVectorsToMaps(
formatAttachRecords(ids, attributes)));
else
attachUsingCustomClass(ids, attributes);
@@ -1200,7 +1200,7 @@ namespace Orm::Tiny::Relations
we have inserted the records, we will touch the relationships if
necessary and the function will return. */
newPivotStatement()->insert(
TinyUtils::Attribute::convertVectorsToMaps(
AttributeUtils::convertVectorsToMaps(
formatAttachRecords(idsWithAttributes)));
else
attachUsingCustomClass(idsWithAttributes);
@@ -1334,7 +1334,7 @@ namespace Orm::Tiny::Relations
int updated = -1;
std::tie(updated, std::ignore) =
newPivotStatementForId(id)->update(
TinyUtils::Attribute::convertVectorToUpdateItem(
AttributeUtils::convertVectorToUpdateItem(
castAttributes(attributes)));
/* It will not touch if attributes size is 0, because this function is called

View File

@@ -12,7 +12,6 @@ TINYORM_BEGIN_COMMON_NAMESPACE
namespace Orm::Tiny::Relations
{
namespace TinyUtils = Orm::Tiny::Utils;
/*! Has one/many relation base class. */
template<class Model, class Related>
@@ -20,6 +19,9 @@ namespace Orm::Tiny::Relations
{
Q_DISABLE_COPY(HasOneOrMany)
/*! Alias for the attribute utils. */
using AttributeUtils = Orm::Tiny::Utils::Attribute;
protected:
/*! Protected constructor. */
HasOneOrMany(std::unique_ptr<Related> &&related, Model &parent,
@@ -199,7 +201,7 @@ namespace Orm::Tiny::Relations
auto newInstance =
this->m_related->newInstance(
TinyUtils::Attribute::joinAttributesForFirstOr(
AttributeUtils::joinAttributesForFirstOr(
attributes, values, this->m_relatedKey));
setForeignAttributesForCreate(newInstance);
@@ -218,7 +220,7 @@ namespace Orm::Tiny::Relations
if (instance)
return *instance;
return create(TinyUtils::Attribute::joinAttributesForFirstOr(
return create(AttributeUtils::joinAttributesForFirstOr(
attributes, values, this->m_relatedKey));
}

View File

@@ -32,6 +32,9 @@ namespace Orm::Tiny
// Used by TinyBuilderProxies::where/latest/oldest/update()
friend BuilderProxies<Model>;
/*! Alias for the attribute utils. */
using AttributeUtils = Orm::Tiny::Utils::Attribute;
public:
/*! Constructor. */
Builder(const QSharedPointer<QueryBuilder> &query, Model &model);
@@ -348,9 +351,8 @@ namespace Orm::Tiny
if (instance)
return *instance;
return newModelInstance(
TinyUtils::Attribute::joinAttributesForFirstOr(
attributes, values, m_model.getKeyName()));
return newModelInstance(AttributeUtils::joinAttributesForFirstOr(
attributes, values, m_model.getKeyName()));
}
template<typename Model>
@@ -363,9 +365,8 @@ namespace Orm::Tiny
return *instance;
auto newInstance =
newModelInstance(
TinyUtils::Attribute::joinAttributesForFirstOr(
attributes, values, m_model.getKeyName()));
newModelInstance(AttributeUtils::joinAttributesForFirstOr(
attributes, values, m_model.getKeyName()));
newInstance.save();

View File

@@ -23,7 +23,6 @@ namespace Query
namespace Tiny
{
namespace TinyUtils = Orm::Tiny::Utils;
/*! Contains proxy methods to the QueryBuilder. */
template<typename Model>
@@ -31,6 +30,8 @@ namespace Tiny
{
Q_DISABLE_COPY(BuilderProxies)
/*! Alias for the attribute utils. */
using AttributeUtils = Orm::Tiny::Utils::Attribute;
/*! JoinClause alias. */
using JoinClause = Orm::Query::JoinClause;
@@ -439,14 +440,14 @@ namespace Tiny
std::optional<QSqlQuery>
BuilderProxies<Model>::insert(const QVector<AttributeItem> &values) const
{
return toBase().insert(TinyUtils::Attribute::convertVectorToMap(values));
return toBase().insert(AttributeUtils::convertVectorToMap(values));
}
template<typename Model>
std::optional<QSqlQuery>
BuilderProxies<Model>::insert(const QVector<QVector<AttributeItem>> &values) const
{
return toBase().insert(TinyUtils::Attribute::convertVectorsToMaps(values));
return toBase().insert(AttributeUtils::convertVectorsToMaps(values));
}
// FEATURE dilemma primarykey, Model::KeyType vs QVariant silverqx
@@ -455,7 +456,7 @@ namespace Tiny
BuilderProxies<Model>::insertGetId(const QVector<AttributeItem> &values,
const QString &sequence) const
{
return toBase().insertGetId(TinyUtils::Attribute::convertVectorToMap(values),
return toBase().insertGetId(AttributeUtils::convertVectorToMap(values),
sequence);
}
@@ -463,8 +464,7 @@ namespace Tiny
std::tuple<int, std::optional<QSqlQuery>>
BuilderProxies<Model>::insertOrIgnore(const QVector<AttributeItem> &values) const
{
return toBase().insertOrIgnore(
TinyUtils::Attribute::convertVectorToMap(values));
return toBase().insertOrIgnore(AttributeUtils::convertVectorToMap(values));
}
template<typename Model>
@@ -472,8 +472,7 @@ namespace Tiny
BuilderProxies<Model>::insertOrIgnore(
const QVector<QVector<AttributeItem>> &values) const
{
return toBase().insertOrIgnore(
TinyUtils::Attribute::convertVectorsToMaps(values));
return toBase().insertOrIgnore(AttributeUtils::convertVectorsToMaps(values));
}
template<typename Model>

View File

@@ -11,7 +11,7 @@ TINY_SYSTEM_HEADER
#include <map>
#if defined(__clang__) || (defined(_MSC_VER) && _MSC_VER <= 1928)
#include <range/v3/algorithm/unique.hpp>
# include <range/v3/algorithm/unique.hpp>
#endif
#include "orm/macros/commonnamespace.hpp"

View File

@@ -9,33 +9,46 @@ TINY_SYSTEM_HEADER
TINYORM_BEGIN_COMMON_NAMESPACE
namespace Orm::Tiny::Utils::Attribute
namespace Orm::Tiny::Utils
{
/*! Convert a AttributeItem QVector to QVariantMap. */
SHAREDLIB_EXPORT QVariantMap
convertVectorToMap(const QVector<AttributeItem> &attributes);
/*! Convert a vector of AttributeItem QVectors to the vector of QVariantMaps. */
SHAREDLIB_EXPORT QVector<QVariantMap>
convertVectorsToMaps(const QVector<QVector<AttributeItem>> &attributesVector);
/*! Convert a AttributeItem QVector to UpdateItem QVector. */
SHAREDLIB_EXPORT QVector<UpdateItem>
convertVectorToUpdateItem(const QVector<AttributeItem> &attributes);
/*! Convert a AttributeItem QVector to UpdateItem QVector. */
SHAREDLIB_EXPORT QVector<UpdateItem>
convertVectorToUpdateItem(QVector<AttributeItem> &&attributes);
/*! Library class for the database attribute. */
class SHAREDLIB_EXPORT Attribute
{
Q_DISABLE_COPY(Attribute)
/*! Remove attributes which have duplicite keys and leave only the last one. */
SHAREDLIB_EXPORT QVector<AttributeItem>
removeDuplicitKeys(const QVector<AttributeItem> &attributes);
public:
/*! Deleted default constructor, this is a pure library class. */
Attribute() = delete;
/*! Deleted destructor. */
~Attribute() = delete;
/*! Join attributes and values for firstOrXx methods. */
SHAREDLIB_EXPORT QVector<AttributeItem>
joinAttributesForFirstOr(const QVector<WhereItem> &attributes,
const QVector<AttributeItem> &values,
const QString &keyName);
/*! Convert a AttributeItem QVector to QVariantMap. */
static QVariantMap
convertVectorToMap(const QVector<AttributeItem> &attributes);
/*! Convert a vector of AttributeItem QVectors to the vector of QVariantMaps. */
static QVector<QVariantMap>
convertVectorsToMaps(const QVector<QVector<AttributeItem>> &attributesVector);
} // namespace Orm::Tiny::Utils::Attribute
/*! Convert a AttributeItem QVector to UpdateItem QVector. */
static QVector<UpdateItem>
convertVectorToUpdateItem(const QVector<AttributeItem> &attributes);
/*! Convert a AttributeItem QVector to UpdateItem QVector. */
static QVector<UpdateItem>
convertVectorToUpdateItem(QVector<AttributeItem> &&attributes);
/*! Remove attributes which have duplicite keys and leave only the last one. */
static QVector<AttributeItem>
removeDuplicitKeys(const QVector<AttributeItem> &attributes);
/*! Join attributes and values for firstOrXx methods. */
static QVector<AttributeItem>
joinAttributesForFirstOr(const QVector<WhereItem> &attributes,
const QVector<AttributeItem> &values,
const QString &keyName);
};
} // namespace Orm::Tiny::Utils
TINYORM_END_COMMON_NAMESPACE

View File

@@ -7,23 +7,50 @@ TINY_SYSTEM_HEADER
#include <QString>
#ifndef TINYORM_DISABLE_TOM
# include <vector>
#endif
#include "orm/macros/commonnamespace.hpp"
#include "orm/macros/export.hpp"
TINYORM_BEGIN_COMMON_NAMESPACE
namespace Orm::Tiny::Utils::String
namespace Orm::Tiny::Utils
{
/*! Convert a string to snake case. */
SHAREDLIB_EXPORT QString toSnake(QString string);
/*! Get the singular form of an English word. */
SHAREDLIB_EXPORT QString singular(const QString &string);
/*! String related library class. */
class SHAREDLIB_EXPORT String
{
Q_DISABLE_COPY(String)
/*! Check if the given string is the number, signed or unsigned. */
SHAREDLIB_EXPORT bool isNumber(const QString &string);
public:
/*! Deleted default constructor, this is a pure library class. */
String() = delete;
/*! Deleted destructor. */
~String() = delete;
} // namespace Orm::Tiny::Utils::String
/*! Convert a string to snake case. */
static QString snake(QString string, QChar delimiter = '_');
/*! Convert a value to studly caps case. */
static QString studly(QString string);
/*! Check if the given string is the number, signed or unsigned. */
static bool isNumber(const QString &string, bool allowFloating = false);
#ifndef TINYORM_DISABLE_TOM
/*! Split a string by the given width (not in the middle of a word). */
static std::vector<QString>
splitStringByWidth(const QString &string, int width);
#endif
#ifndef TINYORM_DISABLE_ORM
/*! Get the singular form of an English word. */
static QString singular(const QString &string);
#endif
};
} // namespace Orm::Tiny::Utils
TINYORM_END_COMMON_NAMESPACE

View File

@@ -11,7 +11,7 @@ TINY_SYSTEM_HEADER
#include <typeinfo>
#ifdef __GNUG__
#include <cxxabi.h>
# include <cxxabi.h>
#endif
#include "orm/macros/commonnamespace.hpp"
@@ -59,6 +59,9 @@ namespace Orm::Utils
/*! Return a pretty function name in the following format: Xyz::function. */
static QString prettyFunction(const QString &function);
/*! Determine whether a string is true bool value (false for "", "0", "false"). */
static bool isTrue(const QString &value);
private:
/*! Class name with or w/o a namespace and w/o template parameters, common
code. */

View File

@@ -4,7 +4,7 @@
// Excluded for the Resource compiler
#ifndef RC_INVOKED
#include "orm/macros/systemheader.hpp"
# include "orm/macros/systemheader.hpp"
TINY_SYSTEM_HEADER
#endif

View File

@@ -5,6 +5,7 @@
#if defined __cplusplus
/* Add C++ includes here */
#include <QDateTime>
#include <QDebug>
#include <QHash>
#include <QMap>
#include <QSharedPointer>
@@ -38,6 +39,7 @@
#include <thread>
#include <tuple>
#include <type_traits>
#include <typeindex>
#include <typeinfo>
#include <unordered_map>
#include <unordered_set>

View File

@@ -30,3 +30,11 @@ CONFIG(release, debug|release): \
# Log queries with a time measurement in debug build
CONFIG(release, debug|release): \
DEFINES *= TINYORM_NO_DEBUG_SQL
# TinyTom related defines
# ---
# Release build
CONFIG(release, debug|release): DEFINES += TINYTOM_NO_DEBUG
# Debug build
CONFIG(debug, debug|release): DEFINES *= TINYTOM_DEBUG

View File

@@ -30,11 +30,25 @@ DEFINES *= QT_NO_CAST_FROM_BYTEARRAY
DEFINES *= QT_USE_QSTRINGBUILDER
DEFINES *= QT_STRICT_ITERATORS
# TinyORM configuration
# ---
# Use extern constants for shared build
CONFIG(shared, dll|shared|static|staticlib) | \
CONFIG(dll, dll|shared|static|staticlib): \
# Support override because inline_constants can be used in the shared build too
!inline_constants: \
CONFIG += extern_constants
# Archive library build (static build)
else: \
CONFIG += inline_constants
# TinyORM defines
# ---
# Release build
CONFIG(release, debug|release): DEFINES *= TINYORM_NO_DEBUG
CONFIG(release, debug|release): DEFINES += TINYORM_NO_DEBUG
# Debug build
CONFIG(debug, debug|release): DEFINES *= TINYORM_DEBUG
@@ -42,9 +56,27 @@ CONFIG(debug, debug|release): DEFINES *= TINYORM_DEBUG
mysql_ping: DEFINES *= TINYORM_MYSQL_PING
# Log queries with a time measurement
CONFIG(release, debug|release): DEFINES *= TINYORM_NO_DEBUG_SQL
CONFIG(release, debug|release): DEFINES += TINYORM_NO_DEBUG_SQL
CONFIG(debug, debug|release): DEFINES *= TINYORM_DEBUG_SQL
# Enable code needed by tests, eg. connection overriding in the Model
!disable_orm:build_tests: \
DEFINES *= TINYORM_TESTS_CODE
# TinyTom related defines
# ---
!disable_tom {
# Release build
CONFIG(release, debug|release): DEFINES += TINYTOM_NO_DEBUG
# Debug build
CONFIG(debug, debug|release): DEFINES *= TINYTOM_DEBUG
# Enable code needed by tests (modify the migrate:status command for tests need)
build_tests: \
DEFINES *= TINYTOM_TESTS_CODE
}
# Platform specific configuration
# ---
win32: include(winconf.pri)
@@ -62,17 +94,3 @@ debug_and_release: {
TINY_RELEASE_TYPE = $$quote(/debug)
}
else: TINY_RELEASE_TYPE =
# Other
# ---
# Use extern constants for shared build
CONFIG(shared, dll|shared|static|staticlib) | \
CONFIG(dll, dll|shared|static|staticlib): \
# Support override because inline_constants can be used in the shared build too
!inline_constants: \
CONFIG += extern_constants
# Archive library build (static build)
else: \
CONFIG += inline_constants

View File

@@ -0,0 +1,2 @@
CONFIG *= disable_tom
DEFINES *= TINYORM_DISABLE_TOM

View File

@@ -14,16 +14,12 @@ defineTest(tiny_resource_and_manifest) {
isEmpty(2): resourcesFolder = $$absolute_path(resources, $$_PRO_FILE_PWD_)
else: resourcesFolder = $$absolute_path($$2)
# Processing of RC file and manifest file for the test?
defined(3, var):$$3: isTest = true
else: isTest = false
# Target's extension
contains(TEMPLATE, ".*app"): targetExt = ".exe"
else:contains(TEMPLATE, ".*lib"): targetExt = ".dll"
# Windows Resource file
rcFile = $$tiny_configure_cmake_rc($$resourcesFolder, $$targetExt, $$isTest)
rcFile = $$tiny_configure_cmake_rc($$resourcesFolder, $$targetExt, $$3, $$4)
# Needed in the RC file, MinGW does not define _DEBUG macro
mingw:CONFIG(debug, debug|release): DEFINES += _DEBUG
@@ -34,8 +30,8 @@ defineTest(tiny_resource_and_manifest) {
# Manifest file
CONFIG -= embed_manifest_dll
# Use the same manifest file for all tests
$$isTest: manifestBasename = TinyTest
# Allow to pass RC/manifest file basename as 3. argument (default is a $$TARGET value)
defined(3, var):!isEmpty(3): manifestBasename = $$3
else: manifestBasename = $$TARGET
# On MSVC use EMBED and on MinGW injected through the RC file
@@ -56,7 +52,7 @@ defineTest(tiny_resource_and_manifest) {
# RC file than manage two practically the same files.
defineReplace(tiny_configure_cmake_rc) {
# All tests use the same test.rc.in file
defined(3, var):$$3: rcBasename = TinyTest
defined(3, var):!isEmpty(3): rcBasename = $$3
else: rcBasename = $$TARGET
rcFile = $$absolute_path($$1/$${rcBasename}.rc.in)
@@ -75,8 +71,9 @@ defineReplace(tiny_configure_cmake_rc) {
tiny_manifest_basename = $$rcBasename
# The same logic for the substitution token, is 'test' for tests and $$TARGET instead
token = $$rcBasename
# Allow to pass a custom token as 4. argument (default is a $$TARGET value)
defined(4, var):!isEmpty(4): token = $$4
else: token = $$rcBasename
rcFileContent = $$cat($$rcFile, blob)

View File

@@ -1,11 +1,29 @@
# Find version numbers in the version header file and assign them to the
# <TARGET>_VERSION_<MAJOR,MINOR,PATCH,TWEAK> and also to the VERSION variable.
defineTest(tiny_version_numbers) {
versionHeader = $$find(HEADERS, "(?:.*\/)?version\.h(?:pp)?$")
exists(versionHeader)|isEmpty(versionHeader) {
# version.hpp for the TinyORM library project
contains(_PRO_FILE_, ".*\/src\/src\.pro$"): \
versionHeader = $$absolute_path("../include/orm/version.hpp")
# version.hpp for the tom example project
else:contains(_PRO_FILE_, ".*\/examples\/tom\/tom\.pro$"): \
versionHeader = $$absolute_path("../tom/include/tom/version.hpp")
else {
# Try to find in the HEADERS
versionHeader = $$find(HEADERS, "(?:.*\/)?version\.h(?:pp)?$")
# Try to find on the INCLUDEPATH
isEmpty(versionHeader) {
versionHeaders = $$files("$$clean_path($$first(INCLUDEPATH))/version.hpp", \
true)
versionHeader = $$first(versionHeaders)
}
}
exists(versionHeader)|isEmpty(versionHeader): \
error( "HEADERS does not contain a version header file version.hpp, needed\
in the tiny_version_numbers.prf." )
}
versionFileContent = $$cat($$quote($$absolute_path($$versionHeader)), lines)
versionTokens = MAJOR MINOR BUGFIX BUILD STATUS
@@ -38,9 +56,8 @@ defineTest(tiny_version_numbers) {
$$hasStatus: versionStatus = $$take_last(versionList)
# Use 3 numbers version on other platforms
!win32 {
!win32: \
$$take_last(versionList)
}
VERSION = $$join(versionList, '.')$$versionStatus
export(VERSION)

View File

@@ -0,0 +1,2 @@
CONFIG *= tom_example
DEFINES *= TINYORM_TOM_EXAMPLE

54
qmake/tom.pri Normal file
View File

@@ -0,0 +1,54 @@
TINYORM_SOURCE_TREE = $$clean_path($$quote($$PWD/..))
TINYTOM_SOURCE_TREE = $$quote($$TINYORM_SOURCE_TREE/tom)
# Qt Common Configuration
# ---
QT *= core sql
CONFIG *= link_prl
include($$TINYORM_SOURCE_TREE/qmake/common.pri)
# Configure TinyORM library
# ---
# everything other is defined in the qmake/common.pri
# Link with the shared library
CONFIG(shared, dll|shared|static|staticlib) | \
CONFIG(dll, dll|shared|static|staticlib): \
DEFINES *= TINYORM_LINKING_SHARED
# File version
# ---
# Find version numbers in the version header file and assign them to the
# <TARGET>_VERSION_<MAJOR,MINOR,PATCH,TWEAK> and also to the VERSION variable.
load(tiny_version_numbers)
tiny_version_numbers()
# Windows resource and manifest files
# ---
# Find version.hpp
tinyRcIncludepath = $$quote($$TINYTOM_SOURCE_TREE/include/)
# Find Windows manifest
mingw: tinyRcIncludepath += $$quote($$TINYTOM_SOURCE_TREE/resources/)
load(tiny_resource_and_manifest)
tiny_resource_and_manifest( \
$$tinyRcIncludepath, $$TINYTOM_SOURCE_TREE/resources, tom, TomExample \
)
# Link against TinyORM library
# ---
INCLUDEPATH *= \
$$quote($$TINYORM_SOURCE_TREE/include/) \
$$quote($$TINYTOM_SOURCE_TREE/include/) \
!isEmpty(TINYORM_BUILD_TREE): \
exists($$TINYORM_BUILD_TREE): {
LIBS += $$quote(-L$$TINYORM_BUILD_TREE/src$${TINY_RELEASE_TYPE}/)
LIBS += -lTinyOrm
}

View File

@@ -1,13 +1,13 @@
#include "orm/concerns/logsqueries.hpp"
#ifdef TINYORM_DEBUG_SQL
#include <QDebug>
# include <QDebug>
#endif
#include "orm/databaseconnection.hpp"
#include "orm/macros/likely.hpp"
#ifdef TINYORM_DEBUG_SQL
#include "orm/utils/query.hpp"
# include "orm/utils/query.hpp"
#endif
#ifdef TINYORM_DEBUG_SQL

View File

@@ -2,7 +2,7 @@
#include <QDateTime>
#if defined(TINYORM_MYSQL_PING)
#include <QDebug>
# include <QDebug>
#endif
#include "orm/query/querybuilder.hpp"
@@ -400,6 +400,14 @@ void DatabaseConnection::disconnect()
m_qtConnectionResolver = nullptr;
}
const SchemaGrammar &DatabaseConnection::getSchemaGrammar()
{
if (!m_schemaGrammar)
useDefaultSchemaGrammar();
return *m_schemaGrammar;
}
std::unique_ptr<SchemaBuilder> DatabaseConnection::getSchemaBuilder()
{
if (!m_schemaGrammar)
@@ -432,7 +440,7 @@ namespace
{
using DriverNameMapType = std::unordered_map<QString, const QString &>;
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
/*! Map Qt driver name to the pretty name. */
Q_GLOBAL_STATIC_WITH_ARGS(DriverNameMapType, DRIVER_NAME_MAP, ({
{QMYSQL, MYSQL_},
{QPSQL, POSTGRESQL},

View File

@@ -13,6 +13,10 @@ LogicError::LogicError(const QString &message)
: std::logic_error(message.toUtf8().constData())
{}
LogicError::LogicError(const std::string &message)
: std::logic_error(message)
{}
} // namespace Orm::Exceptions
TINYORM_END_COMMON_NAMESPACE

View File

@@ -13,6 +13,10 @@ RuntimeError::RuntimeError(const QString &message)
: std::runtime_error(message.toUtf8().constData())
{}
RuntimeError::RuntimeError(const std::string &message)
: std::runtime_error(message)
{}
} // namespace Orm::Exceptions
TINYORM_END_COMMON_NAMESPACE

View File

@@ -26,11 +26,6 @@ SqlError::SqlError(const QString &message, const QSqlError &error, const int /*u
, m_sqlError(error)
{}
const QSqlError &SqlError::getSqlError() const
{
return m_sqlError;
}
QString SqlError::formatMessage(const char *message, const QSqlError &error) const
{
QString result(message);

View File

@@ -1,7 +1,7 @@
#include "orm/mysqlconnection.hpp"
#ifdef TINYORM_MYSQL_PING
#include <QDebug>
# include <QDebug>
#endif
#include <QtSql/QSqlDriver>

View File

@@ -9,11 +9,10 @@
TINYORM_BEGIN_COMMON_NAMESPACE
namespace Orm::Tiny::Utils::Attribute
namespace Orm::Tiny::Utils
{
QVariantMap
convertVectorToMap(const QVector<AttributeItem> &attributes)
QVariantMap Attribute::convertVectorToMap(const QVector<AttributeItem> &attributes)
{
QVariantMap result;
@@ -24,7 +23,7 @@ convertVectorToMap(const QVector<AttributeItem> &attributes)
}
QVector<QVariantMap>
convertVectorsToMaps(const QVector<QVector<AttributeItem>> &attributesVector)
Attribute::convertVectorsToMaps(const QVector<QVector<AttributeItem>> &attributesVector)
{
const auto size = attributesVector.size();
QVector<QVariantMap> result(size);
@@ -37,7 +36,7 @@ convertVectorsToMaps(const QVector<QVector<AttributeItem>> &attributesVector)
}
QVector<UpdateItem>
convertVectorToUpdateItem(const QVector<AttributeItem> &attributes)
Attribute::convertVectorToUpdateItem(const QVector<AttributeItem> &attributes)
{
QVector<UpdateItem> result;
result.reserve(attributes.size());
@@ -49,7 +48,7 @@ convertVectorToUpdateItem(const QVector<AttributeItem> &attributes)
}
QVector<UpdateItem>
convertVectorToUpdateItem(QVector<AttributeItem> &&attributes)
Attribute::convertVectorToUpdateItem(QVector<AttributeItem> &&attributes)
{
QVector<UpdateItem> result;
result.reserve(attributes.size());
@@ -62,7 +61,7 @@ convertVectorToUpdateItem(QVector<AttributeItem> &&attributes)
}
QVector<AttributeItem>
removeDuplicitKeys(const QVector<AttributeItem> &attributes)
Attribute::removeDuplicitKeys(const QVector<AttributeItem> &attributes)
{
const auto size = attributes.size();
std::unordered_set<QString> added(static_cast<std::size_t>(size));
@@ -87,9 +86,9 @@ removeDuplicitKeys(const QVector<AttributeItem> &attributes)
}
QVector<AttributeItem>
joinAttributesForFirstOr(const QVector<WhereItem> &attributes,
const QVector<AttributeItem> &values,
const QString &keyName)
Attribute::joinAttributesForFirstOr(const QVector<WhereItem> &attributes,
const QVector<AttributeItem> &values,
const QString &keyName)
{
// Remove the primary key from attributes
auto attributesFiltered =
@@ -126,6 +125,6 @@ joinAttributesForFirstOr(const QVector<WhereItem> &attributes,
return attributesFiltered + valuesFiltered;
}
} // namespace Orm::Tiny::Utils::Attribute
} // namespace Orm::Tiny::Utils
TINYORM_END_COMMON_NAMESPACE

View File

@@ -1,20 +1,47 @@
#include "orm/tiny/utils/string.hpp"
#include <vector>
#include <QStringList>
#include <cmath>
#include "orm/constants.hpp"
using Orm::Constants::DASH;
using Orm::Constants::DOT;
using Orm::Constants::MINUS;
using Orm::Constants::PLUS;
using Orm::Constants::SPACE;
using Orm::Constants::UNDERSCORE;
TINYORM_BEGIN_COMMON_NAMESPACE
namespace Orm::Tiny::Utils::String
namespace Orm::Tiny::Utils
{
QString toSnake(QString string)
/* This is only one translation unit from the Tiny namespace also used in the tom
project, so I leave it enabled in the build system when the tom is going to build,
I will not extract these 3 used methods to own dll or static library, they simply
will be built into the tinyorm shared library because of this
#ifndef TINYORM_DISABLE_ORM/TOM exists, methods are enabled/disabled on the base of
whether the orm or tom is built. */
/* public */
namespace
{
using SnakeCache = std::unordered_map<QString, QString>;
/*! Snake cache for already computed strings. */
Q_GLOBAL_STATIC(SnakeCache, snakeCache); // NOLINT(readability-redundant-member-init)
} // namespace
QString String::snake(QString string, const QChar delimiter)
{
auto key = string;
if (snakeCache->contains(key))
return (*snakeCache)[key];
// RegExp not used for performance reasons
std::vector<int> positions;
positions.reserve(static_cast<std::size_t>(string.size() / 2) + 2);
@@ -33,23 +60,60 @@ QString toSnake(QString string)
}
// Positions stay valid after inserts because reverse iterators used
std::for_each(positions.crbegin(), positions.crend(), [&string](const int pos)
std::for_each(positions.crbegin(), positions.crend(),
[&string, delimiter](const int pos)
{
string.insert(pos, UNDERSCORE);
string.insert(pos, delimiter);
});
return string.toLower();
return (*snakeCache)[std::move(key)] = string.toLower();;
}
QString singular(const QString &string)
namespace
{
if (!string.endsWith(QChar('s')))
using StudlyCache = std::unordered_map<QString, QString>;
/*! Studly cache for already computed strings. */
Q_GLOBAL_STATIC(StudlyCache, studlyCache); // NOLINT(readability-redundant-member-init)
} // namespace
QString String::studly(QString string)
{
auto value = string.trimmed();
// Nothing to do
if (value.isEmpty())
return string;
return string.chopped(1);
// Cache key
auto key = value;
if (studlyCache->contains(key))
return (*studlyCache)[key];
value.replace(DASH, SPACE)
.replace(UNDERSCORE, SPACE);
auto size = value.size();
// Always upper a first character
if (size > 1)
value[0] = value[0].toUpper();
QString::size_type pos = 0;
while ((pos = value.indexOf(SPACE, pos)) != -1) {
// Avoid out of bound exception
if (++pos >= size)
break;
value[pos] = value[pos].toUpper();
}
return (*studlyCache)[std::move(key)] = value.replace(SPACE, "");
}
bool isNumber(const QString &string)
bool String::isNumber(const QString &string, const bool allowFloating)
{
/* Performance boost was amazing after the QRegularExpression has been removed,
around 50% on the Playground app 👀, from 800ms to 400ms. */
@@ -60,15 +124,152 @@ bool isNumber(const QString &string)
if (string.front() == PLUS || string.front() == MINUS)
++itBegin;
// Only one dot allowed
auto dotAlreadyFound = false;
const auto *nonDigit = std::find_if(itBegin, string.cend(),
[](const auto &ch)
[allowFloating, &dotAlreadyFound](const auto &ch)
{
return !std::isdigit(ch.toLatin1());
// Integer type
if (!allowFloating)
return !std::isdigit(ch.toLatin1());
// Floating-point type
// Only one dot allowed
const auto isDot = ch.toLatin1() == DOT;
const auto result = !std::isdigit(ch.toLatin1()) &&
(!isDot || (isDot && dotAlreadyFound));
if (isDot)
dotAlreadyFound = true;
return result;
});
return nonDigit == string.cend();
}
} // namespace Orm::Tiny::Utils::String
#ifndef TINYORM_DISABLE_TOM
namespace
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
using StringViewType = QStringView;
#else
using StringViewType = QStringRef;
#endif
/*! Split the token to multiple lines by the given width. */
bool splitLongToken(StringViewType token, const int width, QString &line,
std::vector<QString> &lines)
{
auto shouldContinue = false;
const auto spaceSize = line.isEmpty() ? 0 : 1;
if (const auto emptySpace = width - line.size() + spaceSize;
token.size() > emptySpace
) {
// If on the line is still more than 30% of an empty space, use/fill it
if (emptySpace > std::llround(static_cast<float>(width) * 0.3F)) {
// Position where to split the token
auto pos = width - line.size() - spaceSize;
// Don't prepend the space at beginning of the line
if (!line.isEmpty())
line.append(SPACE);
line.append(token.left(pos));
// Cut the appended part
token = token.mid(pos);
}
// In every case no more space on the line here, push to lines
lines.emplace_back(std::move(line));
// Start a new line
line.clear(); // NOLINT(bugprone-use-after-move)
// Process a long token or rest of the token after the previous 30% filling
while (!token.isEmpty()) {
// Token is shorter than the width, indicates processing of the last token
if (token.size() <= width) {
line.append(token); // NOLINT(bugprone-use-after-move)
break;
}
// Fill the whole line
line.append(token.left(width));
// Cut the appended part
token = token.mid(width);
// Push to lines
lines.emplace_back(std::move(line));
// Start a new line
line.clear(); // NOLINT(bugprone-use-after-move)
}
shouldContinue = true;
}
return shouldContinue;
}
} // namespace
/*! Split a string by the given width (not in the middle of a word). */
std::vector<QString> String::splitStringByWidth(const QString &string, const int width)
{
// Nothing to split
if (string.size() <= width)
return {string};
std::vector<QString> lines;
QString line;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
for (auto token : string.tokenize(SPACE)) {
#else
for (auto token : string.splitRef(SPACE)) { // NOLINT(performance-for-range-copy) clazy:exclude=range-loop
#endif
/* If there is still a space on the line then append the token */
if (line.size() + token.size() + 1 <= width) {
// Don't prepend the space at beginning of an empty line
if (!line.isEmpty())
line.append(SPACE);
line.append(token);
continue;
}
/* If a token is longer than the width or an empty space on the current line */
if (splitLongToken(token, width, line, lines))
continue;
// No space on the line, push to lines and start a new line
lines.emplace_back(std::move(line));
// Start a new line
line.clear(); // NOLINT(bugprone-use-after-move)
line.append(token);
}
/* This can happen if a simple append of the token was the last operation, can happen
on the two places above. */
if (!line.isEmpty())
lines.emplace_back(std::move(line));
return lines;
}
#endif
#ifndef TINYORM_DISABLE_ORM
QString String::singular(const QString &string)
{
if (!string.endsWith(QChar('s')))
return string;
return string.chopped(1);
}
#endif
} // namespace Orm::Tiny::Utils
TINYORM_END_COMMON_NAMESPACE

View File

@@ -4,11 +4,11 @@
#if !defined(__clang__) && \
!defined(TINYORM_NO_DEBUG) && defined(_MSC_VER) && !defined(Q_OS_WINRT)
#include <qt_windows.h>
# include <qt_windows.h>
#endif
#if defined(Q_OS_LINUX) && !defined(QT_LINUXBASE)
#include <sys/prctl.h>
# include <sys/prctl.h>
#endif
TINYORM_BEGIN_COMMON_NAMESPACE

View File

@@ -3,12 +3,12 @@
#include <QRegularExpression>
#if !defined(_MSC_VER)
#include <memory>
# include <memory>
#endif
#include "orm/constants.hpp"
#if !defined(_MSC_VER)
#include "orm/exceptions/runtimeerror.hpp"
# include "orm/exceptions/runtimeerror.hpp"
#endif
using Orm::Constants::ASTERISK_C;
@@ -57,6 +57,11 @@ QString Type::prettyFunction(const QString &function)
return QStringLiteral("%1::%2").arg(match.captured(1), match.captured(2));
}
bool Type::isTrue(const QString &value)
{
return !value.isEmpty() && value != "0" && value != "false";
}
QString
Type::classPureBasenameInternal(const std::type_info &typeInfo, const bool withNamespace)
{

View File

@@ -61,6 +61,9 @@ sourcesList += \
$$PWD/orm/tiny/tinytypes.cpp \
$$PWD/orm/tiny/types/syncchanges.cpp \
$$PWD/orm/tiny/utils/attribute.cpp \
!disable_orm|!disable_tom: \
sourcesList += \
$$PWD/orm/tiny/utils/string.cpp \
SOURCES += $$sorted(sourcesList)

View File

@@ -39,10 +39,6 @@ CONFIG(shared, dll|shared|static|staticlib) | \
CONFIG(dll, dll|shared|static|staticlib): \
DEFINES *= TINYORM_BUILDING_SHARED
# Enable code needed by tests, eg. connection overriding in the Model
build_tests: \
DEFINES *= TINYORM_TESTS_CODE
# TinyORM library header and source files
# ---
@@ -50,6 +46,14 @@ build_tests: \
include(../include/include.pri)
include(src.pri)
# TinyTom header and source files
# ---
!disable_tom {
include(../tom/include/include.pri)
include(../tom/src/src.pri)
}
# File version
# ---
@@ -85,8 +89,16 @@ win32-msvc:CONFIG(debug, debug|release) {
# Some info output
# ---
CONFIG(debug, debug|release):!build_pass: message( "Project is built in DEBUG mode." )
CONFIG(release, debug|release):!build_pass: message( "Project is built in RELEASE mode." )
!build_pass {
CONFIG(debug, debug|release): message( "Project is built in DEBUG mode." )
CONFIG(release, debug|release): message( "Project is built in RELEASE mode." )
!disable_orm: message("Build ORM-related source code.")
else: message("Disable ORM-related source code (build the query builder \
only).")
mysql_ping: message("Enable MySQL ping on Orm::MySqlConnection.")
}
# User Configuration
# ---

View File

@@ -39,6 +39,10 @@ using Orm::DB;
using Orm::Exceptions::RuntimeError;
#ifndef TINYORM_SQLITE_DATABASE
# define TINYORM_SQLITE_DATABASE ""
#endif
namespace TestUtils
{
@@ -49,6 +53,14 @@ namespace TestUtils
correctly.
Tests don't fail but are skipped when a connection is not available. */
namespace
{
/*! DatabaseManager instance. */
Q_GLOBAL_STATIC_WITH_ARGS(std::shared_ptr<Orm::DatabaseManager>, db, {nullptr});
}
/* public */
const QStringList &Databases::createConnections(const QStringList &connections)
{
throwIfConnectionsInitialized();
@@ -57,7 +69,7 @@ const QStringList &Databases::createConnections(const QStringList &connections)
/* The default connection is empty for tests, there is no default connection
because it can produce hard to find bugs, I have to be explicit about
the connection which will be used. */
static const auto manager = DB::create(getConfigurations(connections), "");
static const auto manager = *db = DB::create(getConfigurations(connections), "");
static const auto cachedConnectionNames = manager->connectionNames();
@@ -88,6 +100,16 @@ bool Databases::allEnvVariablesEmpty(const std::vector<const char *> &envVariabl
});
}
const std::shared_ptr<Orm::DatabaseManager> &Databases::manager()
{
if (db() == nullptr)
throw RuntimeError("The global static 'db' was already destroyed.");
return *db;
}
/* private */
const Databases::ConfigurationsType &
Databases::getConfigurations(const QStringList &connections)
{

View File

@@ -6,6 +6,11 @@
#include "export.hpp"
namespace Orm
{
class DatabaseManager;
}
namespace TestUtils
{
@@ -34,6 +39,9 @@ namespace TestUtils
/*! Check whether all env. variables are empty. */
static bool allEnvVariablesEmpty(const std::vector<const char *> &envVariables);
/*! Get a reference to the database manager. */
static const std::shared_ptr<Orm::DatabaseManager> &manager();
private:
/*! Obtain configurations for the given connection names. */
static const ConfigurationsType &

View File

@@ -6,6 +6,7 @@
/* Add C++ includes here */
//#include <QCoreApplication>
#include <QDateTime>
//#include <QDebug>
#include <QHash>
#include <QMap>
#include <QSharedPointer>
@@ -38,7 +39,8 @@
//#include <thread>
#include <tuple>
//#include <type_traits>
#include <typeinfo>
//#include <typeindex>
//#include <typeinfo>
//#include <unordered_map>
//#include <vector>
#endif

View File

@@ -1 +1,5 @@
add_subdirectory(orm)
if(TOM)
add_subdirectory(tom)
endif()

View File

@@ -1,4 +1,12 @@
TEMPLATE = subdirs
SUBDIRS = \
subdirsList = \
orm \
!disable_tom: \
subdirsList += \
tom \
SUBDIRS = $$sorted(subdirsList)
unset(subdirsList)

View File

@@ -0,0 +1 @@
add_subdirectory(migrate)

View File

@@ -0,0 +1,18 @@
# migrate auto test
# ---
set(migrate_ns migrate)
set(migrate_target ${migrate_ns})
project(${migrate_ns}
LANGUAGES CXX
)
add_executable(${migrate_target}
tst_migrate.cpp
)
add_test(NAME ${migrate_target} COMMAND ${migrate_target})
include(TinyTestCommon)
tiny_configure_test(${migrate_target} INCLUDE_MIGRATIONS)

View File

@@ -0,0 +1,5 @@
include($$TINYORM_SOURCE_TREE/tests/qmake/common.pri)
include($$TINYORM_SOURCE_TREE/tests/qmake/TinyUtils.pri)
include($$TINYORM_SOURCE_TREE/tests/database/migrations.pri)
SOURCES += tst_migrate.cpp

View File

@@ -0,0 +1,585 @@
#include <QCoreApplication>
#include <QtTest>
#include "tom/application.hpp"
#include "tom/commands/migrations/statuscommand.hpp"
#include "databases.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"
using namespace Migrations; // NOLINT(google-build-using-namespace)
using TomApplication = Tom::Application;
using Tom::Commands::Migrations::StatusCommand;
using TestUtils::Databases;
class tst_Migrate : public QObject
{
Q_OBJECT
public:
/*! Alias for the test output row. */
using StatusRow = StatusCommand::StatusRow;
/*! Type used for comparing results of the status command. */
using Status = std::vector<StatusRow>;
private slots:
void initTestCase();
void cleanup() const;
void reset() const;
void migrate() const;
void migrate_Step() const;
void rollback_OnMigrate() const;
void rollback_OnMigrateWithStep() const;
void rollback_Step_OnMigrate() const;
void rollback_Step_OnMigrateWithStep() const;
void refresh_OnMigrate() const;
void refresh_OnMigrateWithStep() const;
void refresh_Step() const;
void refresh_StepMigrate() const;
void refresh_Step_StepMigrate() const;
// NOLINTNEXTLINE(readability-redundant-access-specifiers)
private:
/*! Prepare arguments and invoke runCommand(). */
[[nodiscard]] int
invokeCommand(const QString &name, std::vector<const char *> &&arguments = {}) const;
/*! Create a tom application instance and invoke the given command. */
int runCommand(int &argc, const std::vector<const char *> &argv) const;
/*! Invoke the status command to obtain results. */
inline int invokeTestStatusCommand() const;
/*! Get result of the last status command. */
Status status() const;
/*! Create a status object for comparing with the result of the status(). */
Status createStatus(std::initializer_list<StatusRow> rows) const;
/*! Create a status object to be equal after complete rollback. */
Status createResetStatus() const;
/*! Connection name used in this test case. */
QString m_connection {};
};
/*! Alias for the test output row. */
using Status = tst_Migrate::Status;
/*! Type used for comparing results of the status command. */
using StatusRow = tst_Migrate::StatusRow;
/* Extracted common code to re-use. */
namespace
{
// Status
inline const auto *Yes = "Yes";
inline const auto *No = "No";
// Batches
inline const auto *s_1 = "1";
inline const auto *s_2 = "2";
inline const auto *s_3 = "3";
inline const auto *s_4 = "4";
// Migration names
inline const auto *
s_2014_10_12_000000_create_posts_table =
"2014_10_12_000000_create_posts_table";
inline const auto *
s_2014_10_12_100000_add_factor_column_to_posts_table =
"2014_10_12_100000_add_factor_column_to_posts_table";
inline const auto *
s_2014_10_12_200000_create_properties_table =
"2014_10_12_200000_create_properties_table";
inline const auto *
s_2014_10_12_300000_create_phones_table =
"2014_10_12_300000_create_phones_table";
// Fully migrated w/o --step
inline const std::initializer_list<StatusRow>
FullyMigrated = {
{Yes, s_2014_10_12_000000_create_posts_table, s_1},
{Yes, s_2014_10_12_100000_add_factor_column_to_posts_table, s_1},
{Yes, s_2014_10_12_200000_create_properties_table, s_1},
{Yes, s_2014_10_12_300000_create_phones_table, s_1},
};
// Fully migrated with --step
inline const std::initializer_list<StatusRow>
FullyStepMigrated = {
{Yes, s_2014_10_12_000000_create_posts_table, s_1},
{Yes, s_2014_10_12_100000_add_factor_column_to_posts_table, s_2},
{Yes, s_2014_10_12_200000_create_properties_table, s_3},
{Yes, s_2014_10_12_300000_create_phones_table, s_4},
};
} // namespace
/* private slots */
void tst_Migrate::initTestCase()
{
m_connection = Databases::createConnection(Databases::MYSQL);
if (m_connection.isEmpty())
QSKIP(QStringLiteral("%1 autotest skipped, environment variables "
"for '%2' connection have not been defined.")
.arg("tst_Migrate", Databases::MYSQL).toUtf8().constData(), );
/* Modify the migrate:status command to not output a status table to the console but
instead return a result as the vector, this vector is then used for comparing
results. */
TomApplication::enableInUnitTests();
}
void tst_Migrate::cleanup() const
{
/* All test methods need this except for two of them (reset and I don't remember
second), I will not implement special logic to skip this for these two methods. */
{
auto exitCode = invokeCommand("migrate:reset");
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createResetStatus(), status());
}
}
void tst_Migrate::reset() const
{
{
auto exitCode = invokeCommand("migrate:reset");
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createResetStatus(), status());
}
}
void tst_Migrate::migrate() const
{
{
auto exitCode = invokeCommand("migrate");
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createStatus(FullyMigrated), status());
}
}
void tst_Migrate::migrate_Step() const
{
{
auto exitCode = invokeCommand("migrate", {"--step"});
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createStatus(FullyStepMigrated), status());
}
}
void tst_Migrate::rollback_OnMigrate() const
{
{
auto exitCode = invokeCommand("migrate");
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createStatus(FullyMigrated), status());
}
// rollback on previous migrate w/o --step
{
auto exitCode = invokeCommand("migrate:rollback");
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createResetStatus(), status());
}
}
void tst_Migrate::rollback_OnMigrateWithStep() const
{
{
auto exitCode = invokeCommand("migrate", {"--step"});
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createStatus(FullyStepMigrated), status());
}
// rollback on previous migrate with --step
{
auto exitCode = invokeCommand("migrate:rollback");
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createStatus({
{Yes, s_2014_10_12_000000_create_posts_table, s_1},
{Yes, s_2014_10_12_100000_add_factor_column_to_posts_table, s_2},
{Yes, s_2014_10_12_200000_create_properties_table, s_3},
{No, s_2014_10_12_300000_create_phones_table},
}), status());
}
}
void tst_Migrate::rollback_Step_OnMigrate() const
{
{
auto exitCode = invokeCommand("migrate");
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createStatus(FullyMigrated), status());
}
// rollback on previous migrate w/o --step
{
auto exitCode = invokeCommand("migrate:rollback", {"--step=2"});
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createStatus({
{Yes, s_2014_10_12_000000_create_posts_table, s_1},
{Yes, s_2014_10_12_100000_add_factor_column_to_posts_table, s_1},
{No, s_2014_10_12_200000_create_properties_table},
{No, s_2014_10_12_300000_create_phones_table},
}), status());
}
}
void tst_Migrate::rollback_Step_OnMigrateWithStep() const
{
{
auto exitCode = invokeCommand("migrate", {"--step"});
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createStatus(FullyStepMigrated), status());
}
// rollback on previous migrate with --step
{
auto exitCode = invokeCommand("migrate:rollback", {"--step=2"});
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createStatus({
{Yes, s_2014_10_12_000000_create_posts_table, s_1},
{Yes, s_2014_10_12_100000_add_factor_column_to_posts_table, s_2},
{No, s_2014_10_12_200000_create_properties_table},
{No, s_2014_10_12_300000_create_phones_table},
}), status());
}
}
void tst_Migrate::refresh_OnMigrate() const
{
{
auto exitCode = invokeCommand("migrate");
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createStatus(FullyMigrated), status());
}
// refresh on previous migrate w/o --step
{
auto exitCode = invokeCommand("migrate:refresh");
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createStatus(FullyMigrated), status());
}
}
void tst_Migrate::refresh_OnMigrateWithStep() const
{
{
auto exitCode = invokeCommand("migrate", {"--step"});
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createStatus(FullyStepMigrated), status());
}
// refresh on previous migrate with --step
{
auto exitCode = invokeCommand("migrate:refresh");
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createStatus(FullyMigrated), status());
}
}
void tst_Migrate::refresh_Step() const
{
{
auto exitCode = invokeCommand("migrate");
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createStatus(FullyMigrated), status());
}
// refresh on previous migrate w/o --step
{
auto exitCode = invokeCommand("migrate:refresh", {"--step=2"});
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createStatus({
{Yes, s_2014_10_12_000000_create_posts_table, s_1},
{Yes, s_2014_10_12_100000_add_factor_column_to_posts_table, s_1},
{Yes, s_2014_10_12_200000_create_properties_table, s_2},
{Yes, s_2014_10_12_300000_create_phones_table, s_2},
}), status());
}
}
void tst_Migrate::refresh_StepMigrate() const
{
{
auto exitCode = invokeCommand("migrate");
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createStatus(FullyMigrated), status());
}
// refresh on previous migrate w/o --step
{
auto exitCode = invokeCommand("migrate:refresh", {"--step-migrate"});
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createStatus(FullyStepMigrated), status());
}
}
void tst_Migrate::refresh_Step_StepMigrate() const
{
{
auto exitCode = invokeCommand("migrate");
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createStatus(FullyMigrated), status());
}
// refresh on previous migrate w/o --step
{
auto exitCode = invokeCommand("migrate:refresh", {"--step=2", "--step-migrate"});
QVERIFY(exitCode == EXIT_SUCCESS);
}
{
auto exitCode = invokeTestStatusCommand();
QVERIFY(exitCode == EXIT_SUCCESS);
QCOMPARE(createStatus({
{Yes, s_2014_10_12_000000_create_posts_table, s_1},
{Yes, s_2014_10_12_100000_add_factor_column_to_posts_table, s_1},
{Yes, s_2014_10_12_200000_create_properties_table, s_2},
{Yes, s_2014_10_12_300000_create_phones_table, s_3},
}), status());
}
}
/* private */
int tst_Migrate::invokeCommand(const QString &name,
std::vector<const char *> &&arguments) const
{
static const auto connectionTmpl = QStringLiteral("--database=%1");
// Prepare fake argc and argv
const auto nameArr = name.toUtf8();
// FUTURE tests tom, when the schema builder will support more db drivers, I can run it on all supported connections, code will look like in the tst_querybuilder.cpp, then I will fetch connection name in every test method using QFETCH_GLOBAL() and I will pass this connection name to the invokeCommand(), so I will discard m_connection and will use method parameter connection here silverqx
/* Schema builder is implemented only for the MySQL driver, so I can use m_connection
here as the default connection. */
// DB connection to use
const auto connectionArr = connectionTmpl.arg(m_connection).toUtf8();
std::vector<const char *> argv {
#ifdef _WIN32
"tom.exe",
#else
"tom",
#endif
nameArr.constData(),
connectionArr.constData(),
};
std::ranges::move(arguments, std::back_inserter(argv));
int argc = static_cast<int>(argv.size());
return runCommand(argc, argv);
}
int tst_Migrate::runCommand(int &argc, const std::vector<const char *> &argv) const
{
try {
// env. should be always development so passed {} for env. name
return TomApplication(argc, const_cast<char **>(argv.data()),
Databases::manager(), {})
.migrations<
_2014_10_12_000000_create_posts_table,
_2014_10_12_100000_add_factor_column_to_posts_table,
_2014_10_12_200000_create_properties_table,
_2014_10_12_300000_create_phones_table>()
// Fire it up 🔥🚀✨
.runWithArguments({argv.cbegin(), argv.cend()});
} catch (const std::exception &e) {
TomApplication::logException(e, true);
}
return EXIT_FAILURE;
}
int tst_Migrate::invokeTestStatusCommand() const
{
return invokeCommand("migrate:status");
}
Status tst_Migrate::status() const
{
return TomApplication::status();
}
Status tst_Migrate::createStatus(std::initializer_list<StatusRow> rows) const
{
return Status({rows});
}
Status tst_Migrate::createResetStatus() const
{
return Status({
{No, s_2014_10_12_000000_create_posts_table},
{No, s_2014_10_12_100000_add_factor_column_to_posts_table},
{No, s_2014_10_12_200000_create_properties_table},
{No, s_2014_10_12_300000_create_phones_table},
});
}
QTEST_MAIN(tst_Migrate)
#include "tst_migrate.moc"

View File

@@ -0,0 +1,4 @@
TEMPLATE = subdirs
SUBDIRS = \
migrate \

View File

@@ -21,6 +21,15 @@ if(BUILD_SHARED_LIBS)
target_compile_definitions(version PRIVATE TINYTEST_VERSION_IS_SHARED_BUILD)
endif()
if(TOM_EXAMPLE)
target_compile_definitions(version PRIVATE TINYTOM_EXAMPLE)
# To find tom/include/version.hpp
target_include_directories(version PRIVATE
"$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/tom/include>"
)
endif()
target_include_directories(version PRIVATE
"$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/${TINY_BUILD_GENDIR}/include>"
)

View File

@@ -4,5 +4,6 @@
#define TINYTEST_VERSION_TINYORM_PATH "$<TARGET_FILE:@TinyOrm_target@>"
#define TINYTEST_VERSION_TINYUTILS_PATH "$<TARGET_FILE:@TinyUtils_target@>"
#define TINYTEST_VERSION_TOMEXAMPLE_PATH "$<$<TARGET_EXISTS:@TomExample_target@>:$<TARGET_FILE:@TomExample_target@>>"
#endif // TINYTESTS_VERSIONDEBUG_CMAKE_HPP

View File

@@ -4,5 +4,6 @@
#define TINYTEST_VERSION_TINYORM_PATH \"$${TINYTEST_VERSION_TINYORM_PATH}\"
#define TINYTEST_VERSION_TINYUTILS_PATH \"$${TINYTEST_VERSION_TINYUTILS_PATH}\"
#define TINYTEST_VERSION_TOMEXAMPLE_PATH \"$${TINYTEST_VERSION_TOMEXAMPLE_PATH}\"
#endif // TINYTEST_VERSIONDEBUG_QMAKE_HPP

View File

@@ -2,7 +2,7 @@
#include <QtTest>
#if defined(_WIN32) && defined(TINYTEST_VERSION_IS_SHARED_BUILD)
#include <qt_windows.h>
# include <qt_windows.h>
#endif
#include "fs.hpp"
@@ -11,6 +11,10 @@
#include "orm/version.hpp"
// TinyUtils
#include "version.hpp"
// Tom example
#ifdef TINYTOM_EXAMPLE
# include "tom/version.hpp"
#endif
// Used by checkFileVersion_*() tests
#if defined(_WIN32) && defined(TINYTEST_VERSION_IS_SHARED_BUILD)
@@ -22,6 +26,7 @@
#else
# define TINYTEST_VERSION_TINYORM_PATH
# define TINYTEST_VERSION_TINYUTILS_PATH
# define TINYTEST_VERSION_TOMEXAMPLE_PATH
#endif
#if defined(_WIN32)
@@ -35,9 +40,15 @@ class tst_Version : public QObject
private slots:
void versions_TinyOrm() const;
void versions_TinyUtils() const;
#ifdef TINYTOM_EXAMPLE
void versions_TomExample() const;
#endif
void checkFileVersion_TinyOrm() const;
void checkFileVersion_TinyUtils() const;
#ifdef TINYTOM_EXAMPLE
void checkFileVersion_TomExample() const;
#endif
#if defined(_WIN32) && defined(TINYTEST_VERSION_IS_SHARED_BUILD)
// NOLINTNEXTLINE(readability-redundant-access-specifiers)
@@ -125,6 +136,43 @@ void tst_Version::versions_TinyUtils() const
QCOMPARE(TINYUTILS_VERSION, version);
}
#ifdef TINYTOM_EXAMPLE
void tst_Version::versions_TomExample() const
{
// Test types
QCOMPARE(typeid (TINYTOM_VERSION_MAJOR), typeid (int));
QCOMPARE(typeid (TINYTOM_VERSION_MINOR), typeid (int));
QCOMPARE(typeid (TINYTOM_VERSION_BUGFIX), typeid (int));
QCOMPARE(typeid (TINYTOM_VERSION_BUILD), typeid (int));
// Individual version numbers have to be greater than zero
QVERIFY(TINYTOM_VERSION_MAJOR >= 0);
QVERIFY(TINYTOM_VERSION_MINOR >= 0);
QVERIFY(TINYTOM_VERSION_BUGFIX >= 0);
QVERIFY(TINYTOM_VERSION_BUILD >= 0);
// Project and File Version strings
QString versionStr = QString::number(TINYTOM_VERSION_MAJOR) + QChar('.') +
QString::number(TINYTOM_VERSION_MINOR) + QChar('.') +
QString::number(TINYTOM_VERSION_BUGFIX);
QString fileVersionStr = versionStr + QChar('.') +
QString::number(TINYTOM_VERSION_BUILD);
if constexpr (TINYTOM_VERSION_BUILD > 0)
versionStr += QChar('.') + QString::number(TINYTOM_VERSION_BUILD);
versionStr += TINYTOM_VERSION_STATUS;
QCOMPARE(TINYTOM_FILEVERSION_STR, fileVersionStr);
QCOMPARE(TINYTOM_VERSION_STR, versionStr);
QCOMPARE(TINYTOM_VERSION_STR_2, QChar('v') + versionStr);
// Project Version number, to check API compatibility
const auto version = TINYTOM_VERSION_MAJOR * 10000 +
TINYTOM_VERSION_MINOR * 100 +
TINYTOM_VERSION_BUGFIX;
QCOMPARE(TINYTOM_VERSION, version);
}
#endif
void tst_Version::checkFileVersion_TinyOrm() const
{
#if !defined(_WIN32)
@@ -167,6 +215,29 @@ void tst_Version::checkFileVersion_TinyUtils() const
#endif
}
#ifdef TINYTOM_EXAMPLE
void tst_Version::checkFileVersion_TomExample() const
{
#if !defined(_WIN32)
QSKIP("checkFileVersion_*() related tests are supported on MSVC only.", );
#elif !defined(TINYTEST_VERSION_IS_SHARED_BUILD)
QSKIP("checkFileVersion_*() related tests are enabled for shared builds only.", );
#else
const auto fileVersions =
getExeVersionString(Fs::absolutePath(TINYTEST_VERSION_TOMEXAMPLE_PATH));
// Project and File Version strings
const QString versionStr = QString::number(TINYTOM_VERSION_MAJOR) + QChar('.') +
QString::number(TINYTOM_VERSION_MINOR) + QChar('.') +
QString::number(TINYTOM_VERSION_BUGFIX) + QChar('.') +
QString::number(TINYTOM_VERSION_BUILD);
QCOMPARE(fileVersions.productVersion, versionStr);
QCOMPARE(fileVersions.fileVersion, fileVersions.productVersion);
#endif
}
#endif
#if defined(_WIN32) && defined(TINYTEST_VERSION_IS_SHARED_BUILD)
tst_Version::FileVersions
tst_Version::getExeVersionString(const QString &fileName) const

View File

@@ -7,6 +7,9 @@ SOURCES = tst_version.cpp
win32 {
DEFINES += TINYTEST_VERSION_IS_QMAKE
tom_example:!disable_tom: \
DEFINES += TINYTOM_EXAMPLE
CONFIG(shared, dll|shared|static|staticlib) | \
CONFIG(dll, dll|shared|static|staticlib): \
DEFINES += TINYTEST_VERSION_IS_SHARED_BUILD
@@ -15,6 +18,8 @@ win32 {
$$quote($${TINYORM_BUILD_TREE}/src$${TINY_RELEASE_TYPE}/TinyOrm0.dll)
TINYTEST_VERSION_TINYUTILS_PATH = \
$$quote($${TINYORM_BUILD_TREE}/tests/TinyUtils$${TINY_RELEASE_TYPE}/TinyUtils0.dll)
TINYTEST_VERSION_TOMEXAMPLE_PATH = \
$$quote($${TINYORM_BUILD_TREE}/examples/tom$${TINY_RELEASE_TYPE}/tom.exe)
QMAKE_SUBSTITUTES += $$quote(include/versiondebug_qmake.hpp.in)
@@ -22,5 +27,9 @@ win32 {
INCLUDEPATH += $$quote($$OUT_PWD/include/)
# To find tom/include/version.hpp (don't need to include whole qmake/tom.pri)
tom_example:!disable_tom: \
INCLUDEPATH += $$quote($$TINYORM_SOURCE_TREE/tom/include/)
LIBS += -lVersion
}

View File

@@ -6,19 +6,24 @@ win32-g++|win32-clang-g++ {
# Enable ccache wrapper
CONFIG *= tiny_ccache
# Includes
# tabulate
INCLUDEPATH += $$quote(C:/msys64/home/xyz/vcpkg/installed/x64-mingw-dynamic/include/)
QMAKE_CXXFLAGS += -isystem $$shell_quote(C:/msys64/home/xyz/vcpkg/installed/x64-mingw-dynamic/include/)
# Use faster linker
# CONFIG *= use_lld_linker does not work on MinGW
QMAKE_LFLAGS *= -fuse-ld=lld
}
else:win32-msvc {
# Includes
# range-v3
# range-v3 and tabulate
INCLUDEPATH += $$quote(E:/xyz/vcpkg/installed/x64-windows/include/)
}
else:unix {
# Includes
# range-v3
QMAKE_CXXFLAGS += -isystem $$quote(/home/xyz/vcpkg/installed/x64-linux/include/)
# range-v3 and tabulate
QMAKE_CXXFLAGS += -isystem $$shell_quote(/home/xyz/vcpkg/installed/x64-linux/include/)
# Use faster linkers
clang: CONFIG *= use_lld_linker

View File

@@ -0,0 +1,7 @@
INCLUDEPATH += $$PWD
HEADERS += \
$$PWD/migrations/2014_10_12_000000_create_posts_table.hpp \
$$PWD/migrations/2014_10_12_100000_add_factor_column_to_posts_table.hpp \
$$PWD/migrations/2014_10_12_200000_create_properties_table.hpp \
$$PWD/migrations/2014_10_12_300000_create_phones_table.hpp \

View File

@@ -0,0 +1,29 @@
#pragma once
#include <tom/migration.hpp>
namespace Migrations
{
struct _2014_10_12_000000_create_posts_table : Migration
{
/*! Run the migrations. */
void up() const override
{
Schema::create("posts", [](Blueprint &table)
{
table.id();
table.string(NAME);
table.timestamps();
});
}
/*! Reverse the migrations. */
void down() const override
{
Schema::dropIfExists("posts");
}
};
} // namespace Migrations

View File

@@ -0,0 +1,29 @@
#pragma once
#include <tom/migration.hpp>
namespace Migrations
{
struct _2014_10_12_100000_add_factor_column_to_posts_table : Migration
{
/*! Run the migrations. */
void up() const override
{
Schema::table("posts", [](Blueprint &table)
{
table.integer("factor");
});
}
/*! Reverse the migrations. */
void down() const override
{
Schema::table("posts", [](Blueprint &table)
{
table.dropColumn("factor");
});
}
};
} // namespace Migrations

View File

@@ -0,0 +1,29 @@
#pragma once
#include <tom/migration.hpp>
namespace Migrations
{
struct _2014_10_12_200000_create_properties_table : Migration
{
/*! Run the migrations. */
void up() const override
{
Schema::create("properties", [](Blueprint &table)
{
table.id();
table.string(NAME);
table.timestamps();
});
}
/*! Reverse the migrations. */
void down() const override
{
Schema::dropIfExists("properties");
}
};
} // namespace Migrations

View File

@@ -0,0 +1,29 @@
#pragma once
#include <tom/migration.hpp>
namespace Migrations
{
struct _2014_10_12_300000_create_phones_table : Migration
{
/*! Run the migrations. */
void up() const override
{
Schema::create("phones", [](Blueprint &table)
{
table.id();
table.string(NAME);
table.timestamps();
});
}
/*! Reverse the migrations. */
void down() const override
{
Schema::dropIfExists("phones");
}
};
} // namespace Migrations

View File

@@ -13,7 +13,7 @@
#include "models/user.hpp"
#ifdef PROJECT_TINYORM_PLAYGROUND
#include "configuration.hpp"
# include "configuration.hpp"
#endif
namespace Models

View File

@@ -10,6 +10,12 @@ CONFIG(dll, dll|shared|static|staticlib): \
INCLUDEPATH += $$quote($$TINYORM_SOURCE_TREE/include/)
# TinyTom include path
# ---
!disable_tom: \
INCLUDEPATH += $$quote($$TINYORM_SOURCE_TREE/tom/include/)
# Link against TinyORM library
# ---

View File

@@ -31,12 +31,6 @@ DEFINES += PROJECT_TINYORM_TEST
CONFIG(release, debug|release): \
DEFINES *= QT_NO_DEBUG_OUTPUT
# TinyORM library defines
# ---
# Enable code needed by tests, eg connection overriding in the Model
DEFINES *= TINYORM_TESTS_CODE
# Link against TinyORM library (also adds defines and include headers)
# ---
@@ -51,7 +45,7 @@ mingw: tinyRcIncludepath += $$quote($$TINYORM_SOURCE_TREE/tests/resources/)
load(tiny_resource_and_manifest)
tiny_resource_and_manifest($$tinyRcIncludepath, \
$$quote($$TINYORM_SOURCE_TREE/tests/resources), \
true \
TinyTest \
)
unset(tinyRcIncludepath)

38
tom/include/include.pri Normal file
View File

@@ -0,0 +1,38 @@
INCLUDEPATH += $$PWD
HEADERS += \
$$PWD/tom/application.hpp \
$$PWD/tom/commands/command.hpp \
$$PWD/tom/commands/database/wipecommand.hpp \
$$PWD/tom/commands/environmentcommand.hpp \
$$PWD/tom/commands/helpcommand.hpp \
$$PWD/tom/commands/inspirecommand.hpp \
$$PWD/tom/commands/listcommand.hpp \
$$PWD/tom/commands/make/migrationcommand.hpp \
# $$PWD/tom/commands/make/projectcommand.hpp \
$$PWD/tom/commands/make/stubs/migrationstubs.hpp \
$$PWD/tom/commands/make/stubs/projectstubs.hpp \
$$PWD/tom/commands/migrations/freshcommand.hpp \
$$PWD/tom/commands/migrations/installcommand.hpp \
$$PWD/tom/commands/migrations/migratecommand.hpp \
$$PWD/tom/commands/migrations/refreshcommand.hpp \
$$PWD/tom/commands/migrations/resetcommand.hpp \
$$PWD/tom/commands/migrations/rollbackcommand.hpp \
$$PWD/tom/commands/migrations/statuscommand.hpp \
$$PWD/tom/concerns/callscommands.hpp \
$$PWD/tom/concerns/confirmable.hpp \
$$PWD/tom/concerns/interactswithio.hpp \
$$PWD/tom/concerns/printsoptions.hpp \
$$PWD/tom/config.hpp \
$$PWD/tom/exceptions/invalidargumenterror.hpp \
$$PWD/tom/exceptions/invalidtemplateargumenterror.hpp \
$$PWD/tom/exceptions/logicerror.hpp \
$$PWD/tom/exceptions/runtimeerror.hpp \
$$PWD/tom/exceptions/tomerror.hpp \
$$PWD/tom/migration.hpp \
$$PWD/tom/migrationcreator.hpp \
$$PWD/tom/migrationrepository.hpp \
$$PWD/tom/migrator.hpp \
$$PWD/tom/terminal.hpp \
$$PWD/tom/tomtypes.hpp \
$$PWD/tom/version.hpp \

View File

@@ -0,0 +1,259 @@
#pragma once
#ifndef TOM_APPLICATION_HPP
#define TOM_APPLICATION_HPP
#include <orm/macros/systemheader.hpp>
TINY_SYSTEM_HEADER
#include <QCommandLineParser>
#include "tom/config.hpp"
#include "tom/concerns/interactswithio.hpp"
TINYORM_BEGIN_COMMON_NAMESPACE
namespace Orm
{
class DatabaseManager;
}
namespace Tom {
namespace Commands
{
class Command;
class HelpCommand;
class ListCommand;
} // namespace Commands
namespace Concerns
{
class CallsCommands;
class PrintsOptions;
} // namespace Concerns
class Migration;
class MigrationRepository;
class Migrator;
/*! Tom application. */
class SHAREDLIB_EXPORT Application : public Concerns::InteractsWithIO
{
Q_DISABLE_COPY(Application)
// To access saveOptions()
friend Commands::Command;
// To access createCommand()
friend Commands::HelpCommand;
// To access showVersion()
friend Commands::ListCommand;
// To access m_options
friend Concerns::PrintsOptions;
// To access initializeParser() and createCommand()
friend Concerns::CallsCommands;
/*! Alias for the DatabaseManager. */
using DatabaseManager = Orm::DatabaseManager;
public:
/*! Constructor. */
Application(int &argc, char **argv, std::shared_ptr<DatabaseManager> db,
const char *environmentEnvName = "TOM_ENV",
QString migrationTable = QLatin1String("migrations"),
std::vector<std::shared_ptr<Migration>> migrations = {});
/*! Default destructor. */
inline ~Application() = default;
/*! Instantiate/initialize all migration classes. */
template<typename ...M>
Application &migrations();
/*! Run the tom application. */
int run();
/*! Log exception caught in the main exception handler in a current thread. */
static void logException(const std::exception &e, bool noAnsi = false);
/* Getters / Setters */
/*! Get a current application environment. */
inline const QString &environment() const noexcept;
/*! Get database manager. */
inline DatabaseManager &db() const noexcept;
/*! Get command-line parser. */
inline const QCommandLineParser &parser() const noexcept;
/*! Is the application running in an interactive mode? */
inline bool isInteractive() const noexcept;
/*! Obtain current command-line arguments. */
QStringList arguments() const;
/*! Set the migration repository table name. */
inline Application &migrationTable(QString table);
#ifdef TINYTOM_TESTS_CODE
/*! Alias for the test output row from the status command. */
using StatusRow = std::vector<std::string>;
/*! Get result of the status command (used in auto tests). */
static std::vector<StatusRow> status() noexcept;
/*! Enable logic for unit testing? */
static void enableInUnitTests() noexcept;
#endif
protected:
/*! Alias for the commands' base class. */
using Command = Commands::Command;
/*! Fix m_argc/m_argv data members if the argv is empty. */
void fixEmptyArgv();
/*! Processes the specified function at application's normal exit. */
void initializeAtExit() const;
/*! Initialize the command-line parser. */
void initializeParser(QCommandLineParser &parser);
/*! Save a copy of application options passed to the Qt's parser. */
const QList<QCommandLineOption> &
saveOptions(QList<QCommandLineOption> &&options);
/*! Prepend command options before common options (used by the help command). */
QList<QCommandLineOption>
prependOptions(QList<QCommandLineOption> &&options);
/* Run command */
/*! Parse current application's command line. */
void parseCommandLine();
/*! Initialize environment value, order:
development -> value from env. variable -> --env command-line argument. */
void initializeEnvironment();
/*! Obtain command name to run. */
QString getCommandName();
/* Early exit during parse command-line */
/*! Display the version information and exits. */
Q_NORETURN void showVersion() const;
/*! Display the version information. */
void printVersion() const;
/*! Invoke the list command. */
Q_NORETURN void showCommandsList(int exitCode);
/*! Exit the application with post routines. */
Q_NORETURN void exitApplication(int exitCode) const;
/* Commands factory */
/*! Alias for an optional command-line parser reference. */
using OptionalParserRef =
std::optional<std::reference_wrapper<QCommandLineParser>>;
/*! Create command by the given name. */
std::unique_ptr<Command>
createCommand(const QString &command, OptionalParserRef parser = std::nullopt,
bool showHelp = true);
/*! Migration repository instance. */
std::shared_ptr<MigrationRepository> createMigrationRepository();
/*! Migrator instance. */
std::shared_ptr<Migrator> createMigrator();
/* Others */
/*! Get all supported commands list (used by the list command). */
const std::vector<std::shared_ptr<Command>> &createCommandsVector();
/*! Get all supported commands' names. */
const std::vector<const char *> &commandNames() const;
/*! Get arguments list from the m_argv array. */
QStringList prepareArguments() const;
/*! Current application argc. */
int &m_argc;
/*! Current application argv. */
char **m_argv;
/*! DatabaseManager instance. */
std::shared_ptr<DatabaseManager> m_db;
/*! The migration repository instance. */
std::shared_ptr<MigrationRepository> m_repository = nullptr;
/*! The migrator service instance. */
std::shared_ptr<Migrator> m_migrator = nullptr;
/* Only one instance can exist in the whole application, auto tests create their
own QCoreApplication instance so this has to be excluded. */
#ifndef TINYTOM_TESTS_CODE
/*! Qt's application instance. */
QCoreApplication m_qtApplication;
/*! Determine whether the TomApplication has its own QCoreApplication instance. */
bool hasQtApplication = true;
#else
/*! Determine whether the TomApplication has its own QCoreApplication instance. */
bool hasQtApplication = false;
#endif
/*! Command line parser. */
QCommandLineParser m_parser {};
/*! Current environment. */
QString m_environment = QStringLiteral("development");
/*! Environment variable name that holds a current environment value. */
const char *m_environmentEnvName;
/*! Migration repository table name. */
QString m_migrationTable;
/*! Migrations vector to process. */
std::vector<std::shared_ptr<Migration>> m_migrations;
/*! Is this input means interactive? */
bool m_interactive = true;
/*! Application options. */
QList<QCommandLineOption> m_options {};
/* Auto tests helpers */
#ifdef TINYTOM_TESTS_CODE
public:
/*! Run the tom application with the given arguments (used in auto tests). */
int runWithArguments(QStringList &&arguments);
#endif
};
/* public */
template<typename ...Migrations>
Application &Application::migrations()
{
m_migrations = {std::make_shared<Migrations>()...};
// Correct sort order is checked in the Migrator::createMigrationNamesMap()
return *this;
}
/* Getters / Setters */
const QString &Application::environment() const noexcept
{
return m_environment;
}
Application::DatabaseManager &Application::db() const noexcept
{
return *m_db;
}
const QCommandLineParser &Application::parser() const noexcept
{
return m_parser;
}
bool Application::isInteractive() const noexcept
{
return m_interactive;
}
Application &Application::migrationTable(QString table)
{
m_migrationTable = std::move(table);
return *this;
}
} // namespace Tom
TINYORM_END_COMMON_NAMESPACE
#endif // TOM_APPLICATION_HPP

View File

@@ -0,0 +1,153 @@
#pragma once
#ifndef TOM_COMMANDS_COMMAND_HPP
#define TOM_COMMANDS_COMMAND_HPP
#include <orm/macros/systemheader.hpp>
TINY_SYSTEM_HEADER
#include "tom/concerns/callscommands.hpp"
#include "tom/concerns/interactswithio.hpp"
TINYORM_BEGIN_COMMON_NAMESPACE
class QCommandLineOption;
namespace Orm
{
class DatabaseConnection;
}
namespace Tom::Commands
{
/*! Positional argument item for a console command. */
struct PositionalArgument
{
/*! Argument name. */
QString name;
/*! Argument description. */
QString description;
/*! Appended to the Usage line, if empty the name is used. */
QString syntax {};
/*! Is argument optional? */
bool optional = false;
/*! Argument's default value (optional argument only). */
QString defaultValue {};
};
/*! Abstract base class for the console command. */
class Command : public Concerns::CallsCommands,
public Concerns::InteractsWithIO
{
Q_DISABLE_COPY(Command)
public:
/*! Constructor. */
Command(Application &application, QCommandLineParser &parser);
/*! Pure virtual destructor. */
inline virtual ~Command() = 0;
/*! The console command name. */
virtual QString name() const = 0;
/*! The console command description. */
virtual QString description() const = 0;
/*! The console command positional arguments signature. */
inline virtual const std::vector<PositionalArgument> &
positionalArguments() const;
/*! The signature of the console command. */
virtual QList<QCommandLineOption> optionsSignature() const;
/*! The console command help. */
inline virtual QString help() const;
/*! Execute the console command. */
virtual int run();
/*! Execute the console command with the given arguments. */
int runWithArguments(QStringList &&arguments);
/* Getters */
/*! Get the tom application. */
inline Application &application() const noexcept;
/*! Determine whether a command has own positional arguments. */
bool hasPositionalArguments() const;
/*! Determine whether a command has own options. */
bool hasOptions() const;
protected:
/* Getters */
/*! Obtain passed arguments to parse (can come from three sources). */
QStringList passedArguments() const;
/* Parser helpers */
/*! Check whether the option name was set in the parser. */
bool isSet(const QString &name) const;
/*! Returns the option value found for the given option name or empty string. */
QString value(const QString &name) const;
/*! Get a full command-line value option if value is set in the parser. */
QString valueCmd(const QString &name, const QString &key = "") const;
/*! Get a full command-line boolean option if it's set in the parser. */
QString boolCmd(const QString &name, const QString &key = "") const;
/*! Check whether a positional argument at the given index was set. */
bool hasArgument(QList<QString>::size_type index) const;
/*! Get a list of positional arguments. */
QStringList arguments() const;
/*! Get a positional argument at the given index position. */
QString argument(QList<QString>::size_type index) const;
/*! Get a positional argument by the given name. */
QString argument(const QString &name) const;
/*! Get a database connection. */
Orm::DatabaseConnection &connection(const QString &name) const;
/*! Get a command-line parser. */
QCommandLineParser &parser() const noexcept;
/*! Reference to the tom application. */
std::reference_wrapper<Application> m_application;
/*! Command line parser. */
std::reference_wrapper<QCommandLineParser> m_parser;
/*! Passed command's arguments. */
QStringList m_arguments {};
/*! Alias for the QList command-line option size type. */
using OptionsSizeType = QList<QCommandLineOption>::size_type;
/*! Map positional argument names to the index for obtaining values. */
std::unordered_map<QString, OptionsSizeType> m_positionalArguments {};
private:
/*! Initialize positional arguments map. */
void initializePositionalArguments();
/*! Show help if --help argument was passed. */
void checkHelpArgument() const;
/*! Show the error wall and exit the application if the parser fails. */
void showParserError(const QCommandLineParser &parser) const;
};
/* public */
Command::~Command() = default;
const std::vector<PositionalArgument> &Command::positionalArguments() const
{
static const std::vector<PositionalArgument> cached;
return cached;
}
QString Command::help() const
{
return {};
}
Application &Command::application() const noexcept
{
return m_application;
}
} // namespace Tom::Commands
TINYORM_END_COMMON_NAMESPACE
#endif // TOM_COMMANDS_COMMAND_HPP

View File

@@ -0,0 +1,67 @@
#pragma once
#ifndef TOM_COMMANDS_DATABASE_WIPECOMMAND_HPP
#define TOM_COMMANDS_DATABASE_WIPECOMMAND_HPP
#include <orm/macros/systemheader.hpp>
TINY_SYSTEM_HEADER
#include "tom/commands/command.hpp"
#include "tom/concerns/confirmable.hpp"
TINYORM_BEGIN_COMMON_NAMESPACE
namespace Tom::Commands::Database
{
/*! Drop all tables, views, and types. */
class WipeCommand : public Command,
public Concerns::Confirmable
{
Q_DISABLE_COPY(WipeCommand)
/*! Alias for the Command. */
using Command = Commands::Command;
public:
/*! Constructor. */
WipeCommand(Application &application, QCommandLineParser &parser);
/*! Virtual destructor. */
inline ~WipeCommand() override = default;
/*! The console command name. */
inline QString name() const override;
/*! The console command description. */
inline QString description() const override;
/*! The signature of the console command. */
QList<QCommandLineOption> optionsSignature() const override;
/*! Execute the console command. */
int run() override;
protected:
/*! Drop all of the database tables. */
void dropAllTables(const QString &database) const;
/*! Drop all of the database views. */
void dropAllViews(const QString &database) const;
/*! Drop all of the database types. */
void dropAllTypes(const QString &database) const;
};
/* public */
QString WipeCommand::name() const
{
return QStringLiteral("db:wipe");
}
QString WipeCommand::description() const
{
return QLatin1String("Drop all tables, views, and types");
}
} // namespace Tom::Commands::Database
TINYORM_END_COMMON_NAMESPACE
#endif // TOM_COMMANDS_DATABASE_WIPECOMMAND_HPP

View File

@@ -0,0 +1,51 @@
#pragma once
#ifndef TOM_COMMANDS_ENVIRONMENTCOMMAND_HPP
#define TOM_COMMANDS_ENVIRONMENTCOMMAND_HPP
#include <orm/macros/systemheader.hpp>
TINY_SYSTEM_HEADER
#include "tom/commands/command.hpp"
TINYORM_BEGIN_COMMON_NAMESPACE
namespace Tom::Commands
{
/*! Display the current environment. */
class EnvironmentCommand : public Command
{
Q_DISABLE_COPY(EnvironmentCommand)
public:
/*! Constructor. */
EnvironmentCommand(Application &application, QCommandLineParser &parser);
/*! Virtual destructor. */
inline ~EnvironmentCommand() override = default;
/*! The console command name. */
inline QString name() const override;
/*! The console command description. */
inline QString description() const override;
/*! Execute the console command. */
int run() override;
};
/* public */
QString EnvironmentCommand::name() const
{
return QStringLiteral("env");
}
QString EnvironmentCommand::description() const
{
return QLatin1String("Display the current framework environment");
}
} // namespace Tom::Commands
TINYORM_END_COMMON_NAMESPACE
#endif // TOM_COMMANDS_ENVIRONMENTCOMMAND_HPP

View File

@@ -0,0 +1,85 @@
#pragma once
#ifndef TOM_COMMANDS_HELPCOMMAND_HPP
#define TOM_COMMANDS_HELPCOMMAND_HPP
#include <orm/macros/systemheader.hpp>
TINY_SYSTEM_HEADER
#include "tom/commands/command.hpp"
#include "tom/concerns/printsoptions.hpp"
TINYORM_BEGIN_COMMON_NAMESPACE
namespace Tom::Commands
{
/*! Display help for a command. */
class HelpCommand : public Command,
public Concerns::PrintsOptions
{
Q_DISABLE_COPY(HelpCommand)
public:
/*! Constructor. */
HelpCommand(Application &application, QCommandLineParser &parser);
/*! Virtual destructor. */
inline ~HelpCommand() override = default;
/*! The console command name. */
inline QString name() const override;
/*! The console command description. */
inline QString description() const override;
/*! The console command positional arguments signature. */
const std::vector<PositionalArgument> &positionalArguments() const override;
/*! The console command help. */
QString help() const override;
/*! Execute the console command. */
int run() override;
protected:
/*! Create command by the given name. */
std::unique_ptr<Command> createCommand(const QString &name) const;
/*! Validate if all required positional arguments are after optional arguments. */
bool validateRequiredArguments(
const std::vector<PositionalArgument> &arguments) const;
/*! Print description section. */
void printDescriptionSection(const Command &command) const;
/*! Print usage section. */
void printUsageSection(
const QString &commandNameArg, const Command &command,
const std::vector<PositionalArgument> &arguments) const;
/*! Print positional arguments section. */
void printArgumentsSection(
const std::vector<PositionalArgument> &arguments) const;
/*! Get max. positional argument size in all command arguments. */
int argumentsMaxSize(const std::vector<PositionalArgument> &arguments) const;
/*! Print a positional's argument default value part. */
void printArgumentDefaultValue(const PositionalArgument &argument) const;
/*! Print options section. */
int printOptionsSection(const Command &command) const;
/*! Print help section. */
void printHelpSection(const Command &command) const;
};
/* public */
QString HelpCommand::name() const
{
return QStringLiteral("help");
}
QString HelpCommand::description() const
{
return QLatin1String("Display help for a command");
}
} // namespace Tom::Commands
TINYORM_END_COMMON_NAMESPACE
#endif // TOM_COMMANDS_HELPCOMMAND_HPP

View File

@@ -0,0 +1,51 @@
#pragma once
#ifndef TOM_COMMANDS_INSPIRECOMMAND_HPP
#define TOM_COMMANDS_INSPIRECOMMAND_HPP
#include <orm/macros/systemheader.hpp>
TINY_SYSTEM_HEADER
#include "tom/commands/command.hpp"
TINYORM_BEGIN_COMMON_NAMESPACE
namespace Tom::Commands
{
/*! Display an inspiring quote. */
class InspireCommand : public Command
{
Q_DISABLE_COPY(InspireCommand)
public:
/*! Constructor. */
InspireCommand(Application &application, QCommandLineParser &parser);
/*! Virtual destructor. */
inline ~InspireCommand() override = default;
/*! The console command name. */
inline QString name() const override;
/*! The console command description. */
inline QString description() const override;
/*! Execute the console command. */
int run() override;
};
/* public */
QString InspireCommand::name() const
{
return QStringLiteral("inspire");
}
QString InspireCommand::description() const
{
return QLatin1String("Display an inspiring quote");
}
} // namespace Tom::Commands
TINYORM_END_COMMON_NAMESPACE
#endif // TOM_COMMANDS_INSPIRECOMMAND_HPP

View File

@@ -0,0 +1,89 @@
#pragma once
#ifndef TOM_COMMANDS_LISTCOMMAND_HPP
#define TOM_COMMANDS_LISTCOMMAND_HPP
#include <orm/macros/systemheader.hpp>
TINY_SYSTEM_HEADER
#include "tom/commands/command.hpp"
#include "tom/concerns/printsoptions.hpp"
TINYORM_BEGIN_COMMON_NAMESPACE
namespace Tom::Commands
{
/*! List all available commands. */
class ListCommand : public Command,
public Concerns::PrintsOptions
{
Q_DISABLE_COPY(ListCommand)
public:
/*! Constructor. */
ListCommand(Application &application, QCommandLineParser &parser);
/*! Virtual destructor. */
inline ~ListCommand() override = default;
/*! The console command name. */
inline QString name() const override;
/*! The console command description. */
inline QString description() const override;
/*! The console command positional arguments signature. */
const std::vector<PositionalArgument> &positionalArguments() const override;
/*! The signature of the console command. */
QList<QCommandLineOption> optionsSignature() const override;
/*! The console command help. */
QString help() const override;
/*! Execute the console command. */
int run() override;
protected:
/*! Output full commands list. */
int full(const QString &namespaceArg);
/*! Output raw commands list and nothing else (can be consumed by scripts). */
int raw(const QString &namespaceArg);
/* Commands section */
/*! Print commands section. */
void printCommandsSection(const QString &namespaceArg, int optionsMaxSize) const;
/*! Get max. command size in all command names. */
int commandsMaxSize(const std::vector<std::shared_ptr<Command>> &commands,
int optionsMaxSize) const;
/*! Print commands to the console. */
void printCommands(const std::vector<std::shared_ptr<Command>> &commands,
int commandsMaxSize, bool hasNamespaceArg) const;
/*! Print a new namespace section. */
void tryBeginNsSection(QString &renderingNamespace,
const QString &commandName, bool hasNamespaceArg) const;
/*! Get command's namespace from a command name. */
QString commandNamespace(const QString &commandName) const;
/*! Wrapper for the two methods below, helps to avoid one copy. */
const std::vector<std::shared_ptr<Command>> &
getCommandsByNamespace(const QString &name) const;
/*! Obtain all commands in the given namespace. */
std::vector<std::shared_ptr<Command>>
getCommandsInNamespace(const QString &name) const;
};
/* public */
QString ListCommand::name() const
{
return QStringLiteral("list");
}
QString ListCommand::description() const
{
return QLatin1String("List commands");
}
} // namespace Tom::Commands
TINYORM_END_COMMON_NAMESPACE
#endif // TOM_COMMANDS_LISTCOMMAND_HPP

View File

@@ -0,0 +1,72 @@
#pragma once
#ifndef TOM_COMMANDS_MAKE_MIGRATIONCOMMAND_HPP
#define TOM_COMMANDS_MAKE_MIGRATIONCOMMAND_HPP
#include <orm/macros/systemheader.hpp>
TINY_SYSTEM_HEADER
#include "tom/commands/command.hpp"
#include "tom/migrationcreator.hpp"
TINYORM_BEGIN_COMMON_NAMESPACE
namespace Tom::Commands::Make
{
/*! Create a new migration file. */
class MigrationCommand : public Command
{
Q_DISABLE_COPY(MigrationCommand)
/*! Alias for the filesystem path. */
using fspath = std::filesystem::path;
public:
/*! Constructor. */
MigrationCommand(Application &application, QCommandLineParser &parser);
/*! Virtual destructor. */
inline ~MigrationCommand() override = default;
/*! The console command name. */
inline QString name() const override;
/*! The console command description. */
inline QString description() const override;
/*! The console command positional arguments signature. */
const std::vector<PositionalArgument> &positionalArguments() const override;
/*! The signature of the console command. */
QList<QCommandLineOption> optionsSignature() const override;
/*! Execute the console command. */
int run() override;
protected:
/*! Write the migration file to disk. */
void writeMigration(const QString &name, const QString &table,
bool create) const;
/*! Get migration path (either specified by '--path' option or default
location). */
fspath getMigrationPath() const;
/*! The migration creator instance. */
MigrationCreator m_creator {};
};
/* public */
QString MigrationCommand::name() const
{
return QStringLiteral("make:migration");
}
QString MigrationCommand::description() const
{
return QLatin1String("Create a new migration file");
}
} // namespace Tom::Commands::Make
TINYORM_END_COMMON_NAMESPACE
#endif // TOM_COMMANDS_MAKE_MIGRATIONCOMMAND_HPP

View File

@@ -0,0 +1,78 @@
#pragma once
#ifndef TOM_COMMANDS_MAKE_PROJECTCOMMAND_HPP
#define TOM_COMMANDS_MAKE_PROJECTCOMMAND_HPP
#include <orm/macros/systemheader.hpp>
TINY_SYSTEM_HEADER
#include "tom/commands/command.hpp"
//#include "tom/migrationcreator.hpp"
TINYORM_BEGIN_COMMON_NAMESPACE
namespace Tom::Commands::Make
{
/*! Create a new Tom application project. */
class ProjectCommand : public Command
{
Q_DISABLE_COPY(ProjectCommand)
/*! Alias for the filesystem path. */
// using path = std::filesystem::path;
public:
/*! Constructor. */
ProjectCommand(Application &application, QCommandLineParser &parser);
/*! Virtual destructor. */
inline ~ProjectCommand() override = default;
/*! The console command name. */
inline QString name() const override;
/*! The console command description. */
inline QString description() const override;
/*! The console command positional arguments signature. */
const std::vector<PositionalArgument> &positionalArguments() const override;
/*! The signature of the console command. */
QList<QCommandLineOption> signature() const override;
/*! Execute the console command. */
int run() override;
protected:
/*! Write the migration file to disk. */
// void writeMigration(const QString &name, const QString &table,
// bool create) const;
/*! Get migration path (either specified by '--path' option or default
location). */
// inline path getMigrationPath() const;
/*! The migration creator instance. */
// MigrationCreator m_creator {};
};
/* public */
QString ProjectCommand::name() const
{
return QStringLiteral("make:project");
}
QString ProjectCommand::description() const
{
return QLatin1String("Create a new Tom application project");
}
/* protected */
// std::filesystem::path ProjectCommand::getMigrationPath() const
// {
// return path(__FILE__).parent_path().parent_path().parent_path() / "migrations";
// }
} // namespace Tom::Commands::Make
TINYORM_END_COMMON_NAMESPACE
#endif // TOM_COMMANDS_MAKE_PROJECTCOMMAND_HPP

View File

@@ -0,0 +1,110 @@
#pragma once
#ifndef TOM_COMMANDS_MAKE_STUBS_MIGRATIONSTUBS_HPP
#define TOM_COMMANDS_MAKE_STUBS_MIGRATIONSTUBS_HPP
#include <orm/macros/systemheader.hpp>
TINY_SYSTEM_HEADER
#include <orm/macros/commonnamespace.hpp>
TINYORM_BEGIN_COMMON_NAMESPACE
namespace Tom::Commands::Make::Stubs
{
/*! Empty migration stub. */
inline const auto *const MigrationStub = R"T(#pragma once
#include <tom/migration.hpp>
namespace Migrations
{
struct {{ class }} : Migration
{
/*! Run the migrations. */
void up() const override
{
//
}
/*! Reverse the migrations. */
void down() const override
{
//
}
};
} // namespace Migrations
)T";
/*! Migration stub for creating a new table. */
inline const auto *const MigrationCreateStub = R"T(#pragma once
#include <tom/migration.hpp>
namespace Migrations
{
struct {{ class }} : Migration
{
/*! Run the migrations. */
void up() const override
{
Schema::create("{{ table }}", [](Blueprint &table)
{
table.id();
table.timestamps();
});
}
/*! Reverse the migrations. */
void down() const override
{
Schema::dropIfExists("{{ table }}");
}
};
} // namespace Migrations
)T";
/*! Migration stub for updating an existing table. */
inline const auto *const MigrationUpdateStub = R"T(#pragma once
#include <tom/migration.hpp>
namespace Migrations
{
struct {{ class }} : Migration
{
/*! Run the migrations. */
void up() const override
{
Schema::table("{{ table }}", [](Blueprint &table)
{
//
});
}
/*! Reverse the migrations. */
void down() const override
{
Schema::table("{{ table }}", [](Blueprint &table)
{
//
});
}
};
} // namespace Migrations
)T";
} // namespace Tom::Commands::Make::Stubs
TINYORM_END_COMMON_NAMESPACE
#endif // TOM_COMMANDS_MAKE_STUBS_MIGRATIONSTUBS_HPP

View File

@@ -0,0 +1,22 @@
#pragma once
#ifndef TOM_COMMANDS_MAKE_STUBS_PROJECTSTUBS_HPP
#define TOM_COMMANDS_MAKE_STUBS_PROJECTSTUBS_HPP
#include <orm/macros/systemheader.hpp>
TINY_SYSTEM_HEADER
#include <orm/macros/commonnamespace.hpp>
TINYORM_BEGIN_COMMON_NAMESPACE
namespace Tom::Commands::Make::Stubs
{
inline const auto *const XyzStub = R"T(#pragma once
)T";
} // namespace Tom::Commands::Make::Stubs
TINYORM_END_COMMON_NAMESPACE
#endif // TOM_COMMANDS_MAKE_STUBS_PROJECTSTUBS_HPP

View File

@@ -0,0 +1,74 @@
#pragma once
#ifndef TOM_COMMANDS_MIGRATIONS_FRESHCOMMAND_HPP
#define TOM_COMMANDS_MIGRATIONS_FRESHCOMMAND_HPP
#include <orm/macros/systemheader.hpp>
TINY_SYSTEM_HEADER
#include "tom/commands/command.hpp"
#include "tom/concerns/confirmable.hpp"
TINYORM_BEGIN_COMMON_NAMESPACE
namespace Tom
{
class Migrator;
namespace Commands::Migrations
{
/*! Rollback the last database migration/s. */
class FreshCommand : public Command,
public Concerns::Confirmable
{
Q_DISABLE_COPY(FreshCommand)
/*! Alias for the Command. */
using Command = Commands::Command;
public:
/*! Constructor. */
FreshCommand(Application &application, QCommandLineParser &parser,
std::shared_ptr<Migrator> migrator);
/*! Virtual destructor. */
inline ~FreshCommand() override = default;
/*! The console command name. */
inline QString name() const override;
/*! The console command description. */
inline QString description() const override;
/*! The signature of the console command. */
QList<QCommandLineOption> optionsSignature() const override;
/*! Execute the console command. */
int run() override;
protected:
/*! Determine if the developer has requested database seeding. */
bool needsSeeding() const;
/*! Run the database seeder command. */
void runSeeder(QString &&databaseCmd) const;
/*! The migrator service instance. */
std::shared_ptr<Migrator> m_migrator;
};
/* public */
QString FreshCommand::name() const
{
return QStringLiteral("migrate:fresh");
}
QString FreshCommand::description() const
{
return QLatin1String("Drop all tables and re-run all migrations");
}
} // namespace Commands::Migrations
} // namespace Tom
TINYORM_END_COMMON_NAMESPACE
#endif // TOM_COMMANDS_MIGRATIONS_FRESHCOMMAND_HPP

View File

@@ -0,0 +1,64 @@
#pragma once
#ifndef TOM_COMMANDS_MIGRATIONS_INSTALLCOMMAND_HPP
#define TOM_COMMANDS_MIGRATIONS_INSTALLCOMMAND_HPP
#include <orm/macros/systemheader.hpp>
TINY_SYSTEM_HEADER
#include "tom/commands/command.hpp"
TINYORM_BEGIN_COMMON_NAMESPACE
namespace Tom
{
class MigrationRepository;
namespace Commands::Migrations
{
/*! Create the migration database repository. */
class InstallCommand : public Command
{
Q_DISABLE_COPY(InstallCommand)
public:
/*! Constructor. */
InstallCommand(Application &application, QCommandLineParser &parser,
std::shared_ptr<MigrationRepository> repository);
/*! Virtual destructor. */
inline ~InstallCommand() override = default;
/*! The console command name. */
inline QString name() const override;
/*! The console command description. */
inline QString description() const override;
/*! The signature of the console command. */
QList<QCommandLineOption> optionsSignature() const override;
/*! Execute the console command. */
int run() override;
protected:
/*! The repository instance. */
std::shared_ptr<MigrationRepository> m_repository;
};
/* public */
QString InstallCommand::name() const
{
return QStringLiteral("migrate:install");
}
QString InstallCommand::description() const
{
return QLatin1String("Create the migration repository");
}
} // namespace Commands::Migrations
} // namespace Tom
TINYORM_END_COMMON_NAMESPACE
#endif // TOM_COMMANDS_MIGRATIONS_INSTALLCOMMAND_HPP

View File

@@ -0,0 +1,79 @@
#pragma once
#ifndef TOM_COMMANDS_MIGRATIONS_MIGRATECOMMAND_HPP
#define TOM_COMMANDS_MIGRATIONS_MIGRATECOMMAND_HPP
#include <orm/macros/systemheader.hpp>
TINY_SYSTEM_HEADER
#include "tom/commands/command.hpp"
#include "tom/concerns/confirmable.hpp"
TINYORM_BEGIN_COMMON_NAMESPACE
namespace Tom
{
class Migrator;
namespace Commands::Migrations
{
/*! Run the database migrations up/down. */
class MigrateCommand : public Command,
public Concerns::Confirmable
{
Q_DISABLE_COPY(MigrateCommand)
/*! Alias for the Command. */
using Command = Commands::Command;
public:
/*! Constructor. */
MigrateCommand(Application &application, QCommandLineParser &parser,
std::shared_ptr<Migrator> migrator);
/*! Virtual destructor. */
inline ~MigrateCommand() override = default;
/*! The console command name. */
inline QString name() const override;
/*! The console command description. */
inline QString description() const override;
/*! The signature of the console command. */
QList<QCommandLineOption> optionsSignature() const override;
/*! Execute the console command. */
int run() override;
protected:
/*! Prepare the migration database for running. */
void prepareDatabase() const;
/*! Load the schema state to seed the initial database schema structure. */
void loadSchemaState() const;
/*! Determine if the developer has requested database seeding. */
bool needsSeeding() const;
/*! Run the database seeder command. */
void runSeeder() const;
/*! The migrator service instance. */
std::shared_ptr<Migrator> m_migrator;
};
/* public */
QString MigrateCommand::name() const
{
return QStringLiteral("migrate");
}
QString MigrateCommand::description() const
{
return QLatin1String("Run the database migrations");
}
} // namespace Commands::Migrations
} // namespace Tom
TINYORM_END_COMMON_NAMESPACE
#endif // TOM_COMMANDS_MIGRATIONS_MIGRATECOMMAND_HPP

Some files were not shown because too many files have changed in this diff Show More