From ded3daceb3ca49f056805f3ec1d09d4141ce604f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20K=C3=BChl?= Date: Sun, 2 Feb 2025 20:37:12 +0100 Subject: [PATCH] initial matador ng commit --- CMakeLists.txt | 83 +- Todo.md | 47 - backends/CMakeLists.txt | 5 +- backends/mysql/CMakeLists.txt | 37 - backends/mysql/include/mysql_connection.hpp | 61 - backends/mysql/include/mysql_dialect.hpp | 19 - backends/mysql/include/mysql_error.hpp | 20 - .../mysql/include/mysql_parameter_binder.hpp | 74 - .../include/mysql_prepared_result_reader.hpp | 36 - .../mysql/include/mysql_result_reader.hpp | 36 - backends/mysql/include/mysql_statement.hpp | 37 - backends/mysql/src/mysql_connection.cpp | 280 ---- backends/mysql/src/mysql_dialect.cpp | 17 - backends/mysql/src/mysql_error.cpp | 30 - backends/mysql/src/mysql_parameter_binder.cpp | 192 --- .../src/mysql_prepared_result_reader.cpp | 28 - backends/mysql/src/mysql_result_reader.cpp | 35 - backends/mysql/src/mysql_statement.cpp | 53 - backends/mysql/test/CMakeLists.txt | 46 - backends/mysql/test/Connection.hpp.in | 8 - backends/postgres/CMakeLists.txt | 10 +- .../postgres/include/postgres_connection.hpp | 25 +- backends/postgres/include/postgres_error.hpp | 18 +- .../include/postgres_parameter_binder.h | 58 +- .../include/postgres_result_reader.hpp | 59 +- .../postgres/include/postgres_statement.hpp | 8 +- backends/postgres/src/postgres_connection.cpp | 202 ++- backends/postgres/src/postgres_dialect.cpp | 16 +- backends/postgres/src/postgres_error.cpp | 29 +- .../src/postgres_parameter_binder.cpp | 170 +-- .../postgres/src/postgres_result_reader.cpp | 208 ++- backends/postgres/src/postgres_statement.cpp | 39 +- backends/postgres/test/CMakeLists.txt | 49 +- backends/sqlite/CMakeLists.txt | 37 - backends/sqlite/include/sqlite_connection.hpp | 65 - backends/sqlite/include/sqlite_dialect.hpp | 19 - backends/sqlite/include/sqlite_error.hpp | 15 - .../sqlite/include/sqlite_parameter_binder.h | 44 - .../include/sqlite_prepared_result_reader.hpp | 42 - .../sqlite/include/sqlite_result_reader.hpp | 33 - backends/sqlite/include/sqlite_statement.hpp | 31 - backends/sqlite/src/sqlite_connection.cpp | 202 --- backends/sqlite/src/sqlite_dialect.cpp | 19 - backends/sqlite/src/sqlite_error.cpp | 28 - .../sqlite/src/sqlite_parameter_binder.cpp | 126 -- .../src/sqlite_prepared_result_reader.cpp | 114 -- backends/sqlite/src/sqlite_result_reader.cpp | 35 - backends/sqlite/src/sqlite_statement.cpp | 52 - backends/sqlite/test/CMakeLists.txt | 46 - backends/sqlite/test/Connection.hpp.in | 8 - backends/tests/QueryRecordTest.cpp | 402 ------ backends/tests/QueryTest.cpp | 535 ------- backends/tests/SessionTest.cpp | 99 -- backends/tests/StatementTest.cpp | 117 -- backends/tests/TypeTraitsTest.cpp | 131 -- cmake/CPM.cmake | 1269 +++++++++++++++++ cmake/FindMySQL.cmake | 129 -- include/matador/object/basic_object_info.hpp | 40 + include/matador/object/error_code.hpp | 32 + include/matador/object/schema.hpp | 63 + include/matador/object/schema_node.hpp | 75 + .../matador/query/attribute_string_writer.hpp | 63 + .../{sql => query}/basic_condition.hpp | 20 +- include/matador/{sql => query}/condition.hpp | 197 ++- .../{sql => query}/fk_value_extractor.hpp | 20 +- .../query/intermediates/executable_query.hpp | 27 + .../query/intermediates/fetchable_query.hpp | 82 ++ .../query_create_intermediate.hpp | 27 + .../query_delete_from_intermediate.hpp | 29 + .../query_delete_intermediate.hpp | 20 + .../intermediates/query_drop_intermediate.hpp | 18 + .../query_execute_limit_intermediate.hpp | 20 + .../query_execute_offset_intermediate.hpp | 20 + .../query_execute_order_by_intermediate.hpp | 21 + ...y_execute_order_direction_intermediate.hpp | 20 + .../query_execute_where_intermediate.hpp | 22 + .../intermediates/query_from_intermediate.hpp | 40 + .../query_group_by_intermediate.hpp | 20 + .../query_insert_intermediate.hpp | 27 + .../intermediates/query_intermediate.hpp | 21 + .../intermediates/query_into_intermediate.hpp | 43 + .../intermediates/query_join_intermediate.hpp | 32 + .../query_limit_intermediate.hpp | 20 + .../query_offset_intermediate.hpp | 20 + .../query_order_by_intermediate.hpp | 21 + .../query_order_direction_intermediate.hpp | 20 + .../query_select_intermediate.hpp | 20 + .../intermediates/query_set_intermediate.hpp | 29 + .../query_update_intermediate.hpp | 45 + .../query_where_intermediate.hpp | 22 + .../internal/basic_type_to_string_visitor.hpp | 49 + .../matador/query/internal/key_value_pair.hpp | 25 + .../{sql => query/internal}/query_parts.hpp | 90 +- include/matador/query/join_data.hpp | 19 + .../{sql => query}/key_value_generator.hpp | 20 +- include/matador/query/query.hpp | 35 + include/matador/query/query_compiler.hpp | 65 + include/matador/query/query_data.hpp | 21 + include/matador/query/query_intermediates.hpp | 30 + include/matador/{sql => query}/query_part.hpp | 10 +- include/matador/query/query_part_visitor.hpp | 67 + include/matador/query/value_extractor.hpp | 94 ++ include/matador/sql/abstract_sql_logger.hpp | 86 ++ include/matador/sql/any_type.hpp | 36 - .../sql/any_type_to_string_visitor.hpp | 53 - include/matador/sql/any_type_to_visitor.hpp | 38 - include/matador/sql/backend_provider.hpp | 44 +- include/matador/sql/column.hpp | 15 +- include/matador/sql/column_definition.hpp | 77 +- .../sql/column_definition_generator.hpp | 133 -- include/matador/sql/column_generator.hpp | 128 -- include/matador/sql/connection.hpp | 157 +- include/matador/sql/connection_impl.hpp | 42 - include/matador/sql/connection_info.hpp | 65 +- include/matador/sql/connection_pool.hpp | 188 --- include/matador/sql/convert.hpp | 124 -- include/matador/sql/data_type_traits.hpp | 257 ---- include/matador/sql/dialect.hpp | 220 ++- include/matador/sql/dialect_builder.hpp | 1 + include/matador/sql/dialect_token.hpp | 54 + include/matador/sql/entity.hpp | 49 - include/matador/sql/entity_query_builder.hpp | 283 ---- include/matador/sql/error_code.hpp | 43 + include/matador/sql/executor.hpp | 28 + include/matador/sql/field.hpp | 23 +- .../matador/sql/has_many_to_many_relation.hpp | 38 - .../matador/sql/interface/connection_impl.hpp | 57 + .../sql/{ => interface}/parameter_binder.hpp | 20 +- .../sql/interface/query_result_reader.hpp | 35 + .../matador/sql/interface/statement_impl.hpp | 62 + .../sql/internal/object_result_binder.hpp | 137 ++ .../sql/{ => internal}/query_result_impl.hpp | 83 +- include/matador/sql/key_value_pair.hpp | 25 - include/matador/sql/noop_connection.hpp | 26 - .../matador/sql/object_parameter_binder.hpp | 86 +- include/matador/sql/placeholder_generator.hpp | 49 - include/matador/sql/query.hpp | 32 - include/matador/sql/query.puml | 89 -- include/matador/sql/query_builder.hpp | 176 --- include/matador/sql/query_compiler.hpp | 59 - include/matador/sql/query_context.hpp | 24 +- include/matador/sql/query_data.hpp | 23 - include/matador/sql/query_helper.hpp | 24 - include/matador/sql/query_intermediates.hpp | 344 ----- include/matador/sql/query_part_visitor.hpp | 65 - include/matador/sql/query_result.hpp | 50 +- include/matador/sql/query_result_reader.hpp | 43 - include/matador/sql/record.hpp | 10 + .../matador/sql/result_parameter_binder.hpp | 133 -- include/matador/sql/schema.hpp | 89 -- include/matador/sql/session.hpp | 170 --- include/matador/sql/statement.hpp | 183 ++- include/matador/sql/statement_cache.hpp | 37 - include/matador/sql/statement_impl.hpp | 46 - include/matador/sql/table.hpp | 28 +- include/matador/sql/table_definition.hpp | 73 - include/matador/sql/to_value.hpp | 84 -- include/matador/sql/value_extractor.hpp | 76 - include/matador/utils/access.hpp | 70 +- include/matador/utils/attribute_reader.hpp | 44 + include/matador/utils/attribute_writer.hpp | 44 + include/matador/utils/base_class.hpp | 42 + .../matador/utils/basic_type_converter.hpp | 55 + include/matador/utils/basic_types.hpp | 33 + include/matador/utils/cascade_type.hpp | 11 +- include/matador/utils/constraints.hpp | 24 +- include/matador/utils/convert.hpp | 239 ++++ include/matador/utils/data_type_traits.hpp | 25 + include/matador/utils/default_type_traits.hpp | 168 +++ include/matador/utils/di.hpp | 385 +++++ include/matador/utils/enum_mapper.hpp | 7 +- include/matador/utils/error.hpp | 73 + include/matador/utils/errors.hpp | 29 + include/matador/utils/export.hpp | 19 + include/matador/utils/fetch_type.hpp | 6 +- include/matador/utils/field_attributes.hpp | 65 +- include/matador/utils/foreign_attributes.hpp | 12 +- include/matador/utils/identifier.hpp | 233 +-- include/matador/utils/library.hpp | 20 +- include/matador/utils/logger.hpp | 88 -- include/matador/utils/macro_map.hpp | 42 - include/matador/utils/os.hpp | 109 +- .../matador/{sql => utils}/placeholder.hpp | 4 +- include/matador/utils/result.hpp | 130 +- include/matador/utils/singleton.hpp | 45 + include/matador/utils/string.hpp | 78 +- include/matador/utils/types.hpp | 24 +- include/matador/{sql => utils}/value.hpp | 55 +- include/matador/utils/version.hpp | 50 + source/CMakeLists.txt | 2 + source/core/CMakeLists.txt | 57 + source/core/object/basic_object_info.cpp | 13 + source/core/object/error_code.cpp | 37 + source/core/object/schema.cpp | 98 ++ source/core/object/schema_node.cpp | 60 + source/core/utils/default_type_traits.cpp | 184 +++ source/core/utils/error.cpp | 89 ++ source/core/utils/errors.cpp | 31 + source/core/utils/field_attributes.cpp | 38 + {src => source/core}/utils/identifier.cpp | 84 +- {src => source/core}/utils/library.cpp | 31 +- source/core/utils/os.cpp | 360 +++++ source/core/utils/string.cpp | 81 ++ source/core/utils/types.cpp | 61 + source/core/utils/value.cpp | 109 ++ source/core/utils/version.cpp | 100 ++ source/orm/CMakeLists.txt | 130 ++ source/orm/query/attribute_string_writer.cpp | 109 ++ source/orm/query/basic_condition.cpp | 26 + source/orm/query/condition.cpp | 44 + .../orm/query}/fk_value_extractor.cpp | 4 +- .../query/intermediates/executable_query.cpp | 27 + .../query/intermediates/fetchable_query.cpp | 57 + .../query_create_intermediate.cpp | 23 + .../query_delete_from_intermediate.cpp | 13 + .../query_delete_intermediate.cpp | 20 + .../intermediates/query_drop_intermediate.cpp | 18 + .../query_execute_limit_intermediate.cpp | 13 + .../query_execute_offset_intermediate.cpp | 11 + .../query_execute_order_by_intermediate.cpp | 17 + ...y_execute_order_direction_intermediate.cpp | 13 + .../query_execute_where_intermediate.cpp | 22 + .../intermediates/query_from_intermediate.cpp | 53 + .../query_group_by_intermediate.cpp | 14 + .../query_insert_intermediate.cpp | 40 + .../intermediates/query_intermediate.cpp | 12 + .../intermediates/query_into_intermediate.cpp | 17 + .../intermediates/query_join_intermediate.cpp | 13 + .../query_limit_intermediate.cpp | 13 + .../query_offset_intermediate.cpp | 13 + .../query_order_by_intermediate.cpp | 20 + .../query_order_direction_intermediate.cpp | 14 + .../query_select_intermediate.cpp | 21 + .../intermediates/query_set_intermediate.cpp | 13 + .../query_where_intermediate.cpp | 21 + .../internal/basic_type_to_string_visitor.cpp | 9 + source/orm/query/internal/key_value_pair.cpp | 29 + .../orm/query/internal}/query_parts.cpp | 67 +- .../orm/query/internal/query_result_impl.cpp | 61 + .../orm/query}/key_value_generator.cpp | 7 +- source/orm/query/query.cpp | 71 + source/orm/query/query_compiler.cpp | 357 +++++ source/orm/query/query_part.cpp | 12 + .../orm/query/query_update_intermediate.cpp | 23 + source/orm/query/value_extractor.cpp | 124 ++ {src => source/orm}/sql/backend_provider.cpp | 58 +- source/orm/sql/column.cpp | 63 + source/orm/sql/column_definition.cpp | 176 +++ source/orm/sql/connection.cpp | 228 +++ {src => source/orm}/sql/connection_info.cpp | 0 source/orm/sql/dialect.cpp | 136 ++ {src => source/orm}/sql/dialect_builder.cpp | 9 +- source/orm/sql/error_code.cpp | 59 + source/orm/sql/executor.cpp | 5 + {src => source/orm}/sql/field.cpp | 11 +- source/orm/sql/interface/connection_impl.cpp | 20 + .../orm/sql/interface/query_result_reader.cpp | 38 + source/orm/sql/interface/statement_impl.cpp | 41 + .../orm/sql/internal/object_result_binder.cpp | 46 + source/orm/sql/object_parameter_binder.cpp | 30 + {src => source/orm}/sql/query_result.cpp | 6 +- {src => source/orm}/sql/record.cpp | 13 +- source/orm/sql/statement.cpp | 73 + source/orm/sql/table.cpp | 44 + src/CMakeLists.txt | 134 -- src/sql/any_type_to_string_visitor.cpp | 40 - src/sql/basic_condition.cpp | 26 - src/sql/column.cpp | 53 - src/sql/column_definition.cpp | 151 -- src/sql/column_definition_generator.cpp | 31 - src/sql/column_generator.cpp | 33 - src/sql/condition.cpp | 48 - src/sql/connection.cpp | 148 -- src/sql/connection_impl.cpp | 12 - src/sql/convert.cpp | 67 - src/sql/data_type_traits.cpp | 278 ---- src/sql/dialect.cpp | 91 -- src/sql/entity_query_builder.cpp | 38 - src/sql/key_value_pair.cpp | 29 - src/sql/noop_connection.cpp | 52 - src/sql/object_parameter_binder.cpp | 37 - src/sql/placeholder_generator.cpp | 15 - src/sql/query.cpp | 54 - src/sql/query_builder.cpp | 537 ------- src/sql/query_compiler.cpp | 324 ----- src/sql/query_intermediates.cpp | 289 ---- src/sql/query_part.cpp | 13 - src/sql/query_result_impl.cpp | 53 - src/sql/query_result_reader.cpp | 189 --- src/sql/result_parameter_binder.cpp | 42 - src/sql/schema.cpp | 92 -- src/sql/session.cpp | 117 -- src/sql/statement.cpp | 40 - src/sql/statement_cache.cpp | 19 - src/sql/statement_impl.cpp | 19 - src/sql/table_definition.cpp | 148 -- src/sql/value.cpp | 101 -- src/sql/value_extractor.cpp | 29 - src/utils/field_attributes.cpp | 33 - src/utils/foreign_attributes.cpp | 22 - src/utils/logger.cpp | 149 -- src/utils/os.cpp | 208 --- src/utils/string.cpp | 50 - test/AnyTypeToVisitorTest.cpp | 78 - test/BackendProviderTest.cpp | 25 - test/CMakeLists.txt | 58 +- test/ColumnDefinitionGeneratorTest.cpp | 56 - test/ColumnGeneratorTest.cpp | 98 -- test/ConnectionPoolTest.cpp | 154 -- test/ConvertTest.cpp | 9 - test/EntityQueryBuilderTest.cpp | 258 ---- test/QueryBuilderTest.cpp | 250 ---- test/ResultTest.cpp | 95 -- test/TableDefinitionTest.cpp | 49 - test/ValueGeneratorTest.cpp | 29 - test/ValueTest.cpp | 70 - test/backends/ColorEnumTraits.cpp | 23 + test/backends/ColorEnumTraits.hpp | 28 + .../backends}/ConnectionTest.cpp | 9 +- test/backends/QueryBasicTest.cpp | 551 +++++++ test/backends/QueryFixture.cpp | 52 + test/backends/QueryFixture.hpp | 32 + test/backends/QueryRecordTest.cpp | 772 ++++++++++ test/backends/QueryStatementTests.cpp | 293 ++++ test/backends/QueryTest.cpp | 532 +++++++ test/backends/SessionFixture.cpp | 21 + test/backends/SessionFixture.hpp | 29 + test/backends/SessionTest.cpp | 136 ++ .../backends}/StatementCacheTest.cpp | 6 +- test/backends/StatementTest.cpp | 114 ++ test/backends/TypeTraitsTest.cpp | 79 + test/core/CMakeLists.txt | 25 + test/core/object/PrototypeTreeTest.cpp | 35 + test/core/utils/BasicTypeToVisitorTest.cpp | 66 + test/core/utils/ConvertTest.cpp | 160 +++ test/core/utils/DefaultTypeTraitsTest.cpp | 28 + test/core/utils/DependencyInjectionTest.cpp | 113 ++ test/core/utils/FieldAttributeTest.cpp | 32 + test/core/utils/IdentifierTest.cpp | 118 ++ test/core/utils/ResultTest.cpp | 176 +++ test/core/utils/StringTest.cpp | 28 + test/core/utils/VersionTest.cpp | 62 + test/models/airplane.hpp | 40 - test/models/author.hpp | 40 - test/models/book.hpp | 34 - test/models/category.hpp | 25 - test/models/coordinate.hpp | 5 +- test/models/flight.hpp | 40 - test/models/location.hpp | 4 +- test/models/optional.hpp | 30 - test/models/order.hpp | 50 - test/models/order_details.hpp | 28 - test/models/person.hpp | 31 - test/models/product.hpp | 46 - test/models/recipe.hpp | 55 - test/models/student.hpp | 68 - test/models/supplier.hpp | 25 - test/models/types.hpp | 64 + test/orm/CMakeLists.txt | 30 + test/orm/backend/test_backend_service.cpp | 31 + test/orm/backend/test_backend_service.hpp | 22 + test/orm/backend/test_connection.cpp | 80 ++ test/orm/backend/test_connection.hpp | 32 + test/orm/backend/test_parameter_binder.cpp | 23 + test/orm/backend/test_parameter_binder.hpp | 33 + test/orm/backend/test_result_reader.cpp | 92 ++ test/orm/backend/test_result_reader.hpp | 43 + test/orm/backend/test_statement.cpp | 22 + test/orm/backend/test_statement.hpp | 26 + test/orm/query/ConditionTests.cpp | 112 ++ test/orm/query/QueryBuilderTest.cpp | 235 +++ test/orm/query/QueryFixture.cpp | 15 + test/orm/query/QueryFixture.hpp | 21 + test/orm/query/QueryTest.cpp | 36 + test/{ => orm/sql}/ColumnTest.cpp | 21 +- test/{ => orm/sql}/FieldTest.cpp | 5 +- test/utils/auto_reset_event.cpp | 26 - test/utils/auto_reset_event.hpp | 29 - 378 files changed, 14913 insertions(+), 13431 deletions(-) delete mode 100644 Todo.md delete mode 100644 backends/mysql/CMakeLists.txt delete mode 100644 backends/mysql/include/mysql_connection.hpp delete mode 100644 backends/mysql/include/mysql_dialect.hpp delete mode 100644 backends/mysql/include/mysql_error.hpp delete mode 100644 backends/mysql/include/mysql_parameter_binder.hpp delete mode 100644 backends/mysql/include/mysql_prepared_result_reader.hpp delete mode 100644 backends/mysql/include/mysql_result_reader.hpp delete mode 100644 backends/mysql/include/mysql_statement.hpp delete mode 100644 backends/mysql/src/mysql_connection.cpp delete mode 100644 backends/mysql/src/mysql_dialect.cpp delete mode 100644 backends/mysql/src/mysql_error.cpp delete mode 100644 backends/mysql/src/mysql_parameter_binder.cpp delete mode 100644 backends/mysql/src/mysql_prepared_result_reader.cpp delete mode 100644 backends/mysql/src/mysql_result_reader.cpp delete mode 100644 backends/mysql/src/mysql_statement.cpp delete mode 100644 backends/mysql/test/CMakeLists.txt delete mode 100644 backends/mysql/test/Connection.hpp.in delete mode 100644 backends/sqlite/CMakeLists.txt delete mode 100644 backends/sqlite/include/sqlite_connection.hpp delete mode 100644 backends/sqlite/include/sqlite_dialect.hpp delete mode 100644 backends/sqlite/include/sqlite_error.hpp delete mode 100644 backends/sqlite/include/sqlite_parameter_binder.h delete mode 100644 backends/sqlite/include/sqlite_prepared_result_reader.hpp delete mode 100644 backends/sqlite/include/sqlite_result_reader.hpp delete mode 100644 backends/sqlite/include/sqlite_statement.hpp delete mode 100644 backends/sqlite/src/sqlite_connection.cpp delete mode 100644 backends/sqlite/src/sqlite_dialect.cpp delete mode 100644 backends/sqlite/src/sqlite_error.cpp delete mode 100644 backends/sqlite/src/sqlite_parameter_binder.cpp delete mode 100644 backends/sqlite/src/sqlite_prepared_result_reader.cpp delete mode 100644 backends/sqlite/src/sqlite_result_reader.cpp delete mode 100644 backends/sqlite/src/sqlite_statement.cpp delete mode 100644 backends/sqlite/test/CMakeLists.txt delete mode 100644 backends/sqlite/test/Connection.hpp.in delete mode 100644 backends/tests/QueryRecordTest.cpp delete mode 100644 backends/tests/QueryTest.cpp delete mode 100644 backends/tests/SessionTest.cpp delete mode 100644 backends/tests/StatementTest.cpp delete mode 100644 backends/tests/TypeTraitsTest.cpp create mode 100644 cmake/CPM.cmake delete mode 100644 cmake/FindMySQL.cmake create mode 100644 include/matador/object/basic_object_info.hpp create mode 100644 include/matador/object/error_code.hpp create mode 100644 include/matador/object/schema.hpp create mode 100644 include/matador/object/schema_node.hpp create mode 100644 include/matador/query/attribute_string_writer.hpp rename include/matador/{sql => query}/basic_condition.hpp (64%) rename include/matador/{sql => query}/condition.hpp (67%) rename include/matador/{sql => query}/fk_value_extractor.hpp (59%) create mode 100644 include/matador/query/intermediates/executable_query.hpp create mode 100644 include/matador/query/intermediates/fetchable_query.hpp create mode 100644 include/matador/query/intermediates/query_create_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_delete_from_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_delete_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_drop_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_execute_limit_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_execute_offset_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_execute_order_by_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_execute_order_direction_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_execute_where_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_from_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_group_by_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_insert_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_into_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_join_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_limit_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_offset_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_order_by_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_order_direction_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_select_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_set_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_update_intermediate.hpp create mode 100644 include/matador/query/intermediates/query_where_intermediate.hpp create mode 100644 include/matador/query/internal/basic_type_to_string_visitor.hpp create mode 100644 include/matador/query/internal/key_value_pair.hpp rename include/matador/{sql => query/internal}/query_parts.hpp (65%) create mode 100644 include/matador/query/join_data.hpp rename include/matador/{sql => query}/key_value_generator.hpp (75%) create mode 100644 include/matador/query/query.hpp create mode 100644 include/matador/query/query_compiler.hpp create mode 100644 include/matador/query/query_data.hpp create mode 100644 include/matador/query/query_intermediates.hpp rename include/matador/{sql => query}/query_part.hpp (57%) create mode 100644 include/matador/query/query_part_visitor.hpp create mode 100644 include/matador/query/value_extractor.hpp create mode 100644 include/matador/sql/abstract_sql_logger.hpp delete mode 100644 include/matador/sql/any_type.hpp delete mode 100644 include/matador/sql/any_type_to_string_visitor.hpp delete mode 100644 include/matador/sql/any_type_to_visitor.hpp delete mode 100644 include/matador/sql/column_definition_generator.hpp delete mode 100644 include/matador/sql/column_generator.hpp delete mode 100644 include/matador/sql/connection_impl.hpp delete mode 100644 include/matador/sql/connection_pool.hpp delete mode 100644 include/matador/sql/convert.hpp delete mode 100644 include/matador/sql/data_type_traits.hpp create mode 100644 include/matador/sql/dialect_token.hpp delete mode 100644 include/matador/sql/entity.hpp delete mode 100644 include/matador/sql/entity_query_builder.hpp create mode 100644 include/matador/sql/error_code.hpp create mode 100644 include/matador/sql/executor.hpp delete mode 100644 include/matador/sql/has_many_to_many_relation.hpp create mode 100644 include/matador/sql/interface/connection_impl.hpp rename include/matador/sql/{ => interface}/parameter_binder.hpp (61%) create mode 100644 include/matador/sql/interface/query_result_reader.hpp create mode 100644 include/matador/sql/interface/statement_impl.hpp create mode 100644 include/matador/sql/internal/object_result_binder.hpp rename include/matador/sql/{ => internal}/query_result_impl.hpp (53%) delete mode 100644 include/matador/sql/key_value_pair.hpp delete mode 100644 include/matador/sql/noop_connection.hpp delete mode 100644 include/matador/sql/placeholder_generator.hpp delete mode 100644 include/matador/sql/query.hpp delete mode 100644 include/matador/sql/query.puml delete mode 100644 include/matador/sql/query_builder.hpp delete mode 100644 include/matador/sql/query_compiler.hpp delete mode 100644 include/matador/sql/query_data.hpp delete mode 100644 include/matador/sql/query_helper.hpp delete mode 100644 include/matador/sql/query_intermediates.hpp delete mode 100644 include/matador/sql/query_part_visitor.hpp delete mode 100644 include/matador/sql/query_result_reader.hpp delete mode 100644 include/matador/sql/result_parameter_binder.hpp delete mode 100644 include/matador/sql/schema.hpp delete mode 100644 include/matador/sql/session.hpp delete mode 100644 include/matador/sql/statement_cache.hpp delete mode 100644 include/matador/sql/statement_impl.hpp delete mode 100644 include/matador/sql/table_definition.hpp delete mode 100644 include/matador/sql/to_value.hpp delete mode 100644 include/matador/sql/value_extractor.hpp create mode 100644 include/matador/utils/attribute_reader.hpp create mode 100644 include/matador/utils/attribute_writer.hpp create mode 100644 include/matador/utils/base_class.hpp create mode 100644 include/matador/utils/basic_type_converter.hpp create mode 100644 include/matador/utils/basic_types.hpp create mode 100644 include/matador/utils/convert.hpp create mode 100644 include/matador/utils/data_type_traits.hpp create mode 100644 include/matador/utils/default_type_traits.hpp create mode 100644 include/matador/utils/di.hpp create mode 100644 include/matador/utils/error.hpp create mode 100644 include/matador/utils/errors.hpp create mode 100644 include/matador/utils/export.hpp delete mode 100644 include/matador/utils/logger.hpp delete mode 100644 include/matador/utils/macro_map.hpp rename include/matador/{sql => utils}/placeholder.hpp (78%) create mode 100644 include/matador/utils/singleton.hpp rename include/matador/{sql => utils}/value.hpp (55%) create mode 100644 include/matador/utils/version.hpp create mode 100644 source/CMakeLists.txt create mode 100644 source/core/CMakeLists.txt create mode 100644 source/core/object/basic_object_info.cpp create mode 100644 source/core/object/error_code.cpp create mode 100644 source/core/object/schema.cpp create mode 100644 source/core/object/schema_node.cpp create mode 100644 source/core/utils/default_type_traits.cpp create mode 100644 source/core/utils/error.cpp create mode 100644 source/core/utils/errors.cpp create mode 100644 source/core/utils/field_attributes.cpp rename {src => source/core}/utils/identifier.cpp (59%) rename {src => source/core}/utils/library.cpp (61%) create mode 100644 source/core/utils/os.cpp create mode 100644 source/core/utils/string.cpp create mode 100644 source/core/utils/types.cpp create mode 100644 source/core/utils/value.cpp create mode 100644 source/core/utils/version.cpp create mode 100644 source/orm/CMakeLists.txt create mode 100644 source/orm/query/attribute_string_writer.cpp create mode 100644 source/orm/query/basic_condition.cpp create mode 100644 source/orm/query/condition.cpp rename {src/sql => source/orm/query}/fk_value_extractor.cpp (54%) create mode 100644 source/orm/query/intermediates/executable_query.cpp create mode 100644 source/orm/query/intermediates/fetchable_query.cpp create mode 100644 source/orm/query/intermediates/query_create_intermediate.cpp create mode 100644 source/orm/query/intermediates/query_delete_from_intermediate.cpp create mode 100644 source/orm/query/intermediates/query_delete_intermediate.cpp create mode 100644 source/orm/query/intermediates/query_drop_intermediate.cpp create mode 100644 source/orm/query/intermediates/query_execute_limit_intermediate.cpp create mode 100644 source/orm/query/intermediates/query_execute_offset_intermediate.cpp create mode 100644 source/orm/query/intermediates/query_execute_order_by_intermediate.cpp create mode 100644 source/orm/query/intermediates/query_execute_order_direction_intermediate.cpp create mode 100644 source/orm/query/intermediates/query_execute_where_intermediate.cpp create mode 100644 source/orm/query/intermediates/query_from_intermediate.cpp create mode 100644 source/orm/query/intermediates/query_group_by_intermediate.cpp create mode 100644 source/orm/query/intermediates/query_insert_intermediate.cpp create mode 100644 source/orm/query/intermediates/query_intermediate.cpp create mode 100644 source/orm/query/intermediates/query_into_intermediate.cpp create mode 100644 source/orm/query/intermediates/query_join_intermediate.cpp create mode 100644 source/orm/query/intermediates/query_limit_intermediate.cpp create mode 100644 source/orm/query/intermediates/query_offset_intermediate.cpp create mode 100644 source/orm/query/intermediates/query_order_by_intermediate.cpp create mode 100644 source/orm/query/intermediates/query_order_direction_intermediate.cpp create mode 100644 source/orm/query/intermediates/query_select_intermediate.cpp create mode 100644 source/orm/query/intermediates/query_set_intermediate.cpp create mode 100644 source/orm/query/intermediates/query_where_intermediate.cpp create mode 100644 source/orm/query/internal/basic_type_to_string_visitor.cpp create mode 100644 source/orm/query/internal/key_value_pair.cpp rename {src/sql => source/orm/query/internal}/query_parts.cpp (74%) create mode 100644 source/orm/query/internal/query_result_impl.cpp rename {src/sql => source/orm/query}/key_value_generator.cpp (65%) create mode 100644 source/orm/query/query.cpp create mode 100644 source/orm/query/query_compiler.cpp create mode 100644 source/orm/query/query_part.cpp create mode 100644 source/orm/query/query_update_intermediate.cpp create mode 100644 source/orm/query/value_extractor.cpp rename {src => source/orm}/sql/backend_provider.cpp (56%) create mode 100644 source/orm/sql/column.cpp create mode 100644 source/orm/sql/column_definition.cpp create mode 100644 source/orm/sql/connection.cpp rename {src => source/orm}/sql/connection_info.cpp (100%) create mode 100644 source/orm/sql/dialect.cpp rename {src => source/orm}/sql/dialect_builder.cpp (83%) create mode 100644 source/orm/sql/error_code.cpp create mode 100644 source/orm/sql/executor.cpp rename {src => source/orm}/sql/field.cpp (86%) create mode 100644 source/orm/sql/interface/connection_impl.cpp create mode 100644 source/orm/sql/interface/query_result_reader.cpp create mode 100644 source/orm/sql/interface/statement_impl.cpp create mode 100644 source/orm/sql/internal/object_result_binder.cpp create mode 100644 source/orm/sql/object_parameter_binder.cpp rename {src => source/orm}/sql/query_result.cpp (82%) rename {src => source/orm}/sql/record.cpp (90%) create mode 100644 source/orm/sql/statement.cpp create mode 100644 source/orm/sql/table.cpp delete mode 100644 src/CMakeLists.txt delete mode 100644 src/sql/any_type_to_string_visitor.cpp delete mode 100644 src/sql/basic_condition.cpp delete mode 100644 src/sql/column.cpp delete mode 100644 src/sql/column_definition.cpp delete mode 100644 src/sql/column_definition_generator.cpp delete mode 100644 src/sql/column_generator.cpp delete mode 100644 src/sql/condition.cpp delete mode 100644 src/sql/connection.cpp delete mode 100644 src/sql/connection_impl.cpp delete mode 100644 src/sql/convert.cpp delete mode 100644 src/sql/data_type_traits.cpp delete mode 100644 src/sql/dialect.cpp delete mode 100644 src/sql/entity_query_builder.cpp delete mode 100644 src/sql/key_value_pair.cpp delete mode 100644 src/sql/noop_connection.cpp delete mode 100644 src/sql/object_parameter_binder.cpp delete mode 100644 src/sql/placeholder_generator.cpp delete mode 100644 src/sql/query.cpp delete mode 100644 src/sql/query_builder.cpp delete mode 100644 src/sql/query_compiler.cpp delete mode 100644 src/sql/query_intermediates.cpp delete mode 100644 src/sql/query_part.cpp delete mode 100644 src/sql/query_result_impl.cpp delete mode 100644 src/sql/query_result_reader.cpp delete mode 100644 src/sql/result_parameter_binder.cpp delete mode 100644 src/sql/schema.cpp delete mode 100644 src/sql/session.cpp delete mode 100644 src/sql/statement.cpp delete mode 100644 src/sql/statement_cache.cpp delete mode 100644 src/sql/statement_impl.cpp delete mode 100644 src/sql/table_definition.cpp delete mode 100644 src/sql/value.cpp delete mode 100644 src/sql/value_extractor.cpp delete mode 100644 src/utils/field_attributes.cpp delete mode 100644 src/utils/foreign_attributes.cpp delete mode 100644 src/utils/logger.cpp delete mode 100644 src/utils/os.cpp delete mode 100644 src/utils/string.cpp delete mode 100644 test/AnyTypeToVisitorTest.cpp delete mode 100644 test/BackendProviderTest.cpp delete mode 100644 test/ColumnDefinitionGeneratorTest.cpp delete mode 100644 test/ColumnGeneratorTest.cpp delete mode 100644 test/ConnectionPoolTest.cpp delete mode 100644 test/ConvertTest.cpp delete mode 100644 test/EntityQueryBuilderTest.cpp delete mode 100644 test/QueryBuilderTest.cpp delete mode 100644 test/ResultTest.cpp delete mode 100644 test/TableDefinitionTest.cpp delete mode 100644 test/ValueGeneratorTest.cpp delete mode 100644 test/ValueTest.cpp create mode 100644 test/backends/ColorEnumTraits.cpp create mode 100644 test/backends/ColorEnumTraits.hpp rename {backends/tests => test/backends}/ConnectionTest.cpp (63%) create mode 100644 test/backends/QueryBasicTest.cpp create mode 100644 test/backends/QueryFixture.cpp create mode 100644 test/backends/QueryFixture.hpp create mode 100644 test/backends/QueryRecordTest.cpp create mode 100644 test/backends/QueryStatementTests.cpp create mode 100644 test/backends/QueryTest.cpp create mode 100644 test/backends/SessionFixture.cpp create mode 100644 test/backends/SessionFixture.hpp create mode 100644 test/backends/SessionTest.cpp rename {backends/tests => test/backends}/StatementCacheTest.cpp (79%) create mode 100644 test/backends/StatementTest.cpp create mode 100644 test/backends/TypeTraitsTest.cpp create mode 100644 test/core/CMakeLists.txt create mode 100644 test/core/object/PrototypeTreeTest.cpp create mode 100644 test/core/utils/BasicTypeToVisitorTest.cpp create mode 100644 test/core/utils/ConvertTest.cpp create mode 100644 test/core/utils/DefaultTypeTraitsTest.cpp create mode 100644 test/core/utils/DependencyInjectionTest.cpp create mode 100644 test/core/utils/FieldAttributeTest.cpp create mode 100644 test/core/utils/IdentifierTest.cpp create mode 100644 test/core/utils/ResultTest.cpp create mode 100644 test/core/utils/StringTest.cpp create mode 100644 test/core/utils/VersionTest.cpp delete mode 100644 test/models/airplane.hpp delete mode 100644 test/models/author.hpp delete mode 100644 test/models/book.hpp delete mode 100644 test/models/category.hpp delete mode 100644 test/models/flight.hpp delete mode 100644 test/models/optional.hpp delete mode 100644 test/models/order.hpp delete mode 100644 test/models/order_details.hpp delete mode 100644 test/models/person.hpp delete mode 100644 test/models/product.hpp delete mode 100644 test/models/recipe.hpp delete mode 100644 test/models/student.hpp delete mode 100644 test/models/supplier.hpp create mode 100644 test/models/types.hpp create mode 100644 test/orm/CMakeLists.txt create mode 100644 test/orm/backend/test_backend_service.cpp create mode 100644 test/orm/backend/test_backend_service.hpp create mode 100644 test/orm/backend/test_connection.cpp create mode 100644 test/orm/backend/test_connection.hpp create mode 100644 test/orm/backend/test_parameter_binder.cpp create mode 100644 test/orm/backend/test_parameter_binder.hpp create mode 100644 test/orm/backend/test_result_reader.cpp create mode 100644 test/orm/backend/test_result_reader.hpp create mode 100644 test/orm/backend/test_statement.cpp create mode 100644 test/orm/backend/test_statement.hpp create mode 100644 test/orm/query/ConditionTests.cpp create mode 100644 test/orm/query/QueryBuilderTest.cpp create mode 100644 test/orm/query/QueryFixture.cpp create mode 100644 test/orm/query/QueryFixture.hpp create mode 100644 test/orm/query/QueryTest.cpp rename test/{ => orm/sql}/ColumnTest.cpp (72%) rename test/{ => orm/sql}/FieldTest.cpp (91%) delete mode 100644 test/utils/auto_reset_event.cpp delete mode 100644 test/utils/auto_reset_event.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c2b6b0..cc0ed08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,50 +1,57 @@ -cmake_minimum_required(VERSION 3.26) +cmake_minimum_required(VERSION 3.30) + project( - query - VERSION 1.0.0 - DESCRIPTION "SQL query fluent prototype for PostgreSQL, SQLite, MySQL and MSSQL" - LANGUAGES CXX + matador + VERSION 0.9.8 + LANGUAGES C CXX ) +include(cmake/CPM.cmake) +include(CTest) + set(CMAKE_CXX_STANDARD 17) -set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(GCC_CLANG_COMMON_FLAGS "-Wall -Wconversion -Wextra -pedantic -ftemplate-backtrace-limit=0") +SET(GCC_CLANG_COMMON_FLAGS "-Wall -Wextra -pedantic -ftemplate-backtrace-limit=0") +SET(GCC_CLANG_COMMON_FLAGS_DEBUG "-O0 -g -DDEBUG") +SET(GCC_CLANG_COMMON_FLAGS_RELEASE "-O1 -DNDEBUG") +SET(CMAKE_POSITION_INDEPENDENT_CODE ON) -if (WIN32) - add_compile_options(/Zc:preprocessor) -endif() +message(STATUS "C++ Compiler ID: ${CMAKE_CXX_COMPILER_ID}") -list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) - -find_package(ODBC REQUIRED) -find_package(SQLite3 REQUIRED) find_package(PostgreSQL REQUIRED) -find_package(MySQL REQUIRED) -message(STATUS "Found ODBC config ${ODBC_CONFIG}") -message(STATUS "Adding ODBC include directory: ${ODBC_INCLUDE_DIRS}") -message(STATUS "Adding ODBC libs: ${ODBC_LIBRARIES}") +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + message(STATUS "GCC detected - Adding compiler flags") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_CLANG_COMMON_FLAGS}") + set(CMAKE_CXX_FLAGS_DEBUG "${GCC_CLANG_COMMON_FLAGS_DEBUG}") + set(CMAKE_CXX_FLAGS_RELEASE "${GCC_CLANG_COMMON_FLAGS_RELEASE}") +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + MESSAGE(STATUS "MSVC detected - Adding compiler flags") + SET(CMAKE_CXX_FLAGS "/W3 /EHsc /bigobj") + SET(CMAKE_CXX_FLAGS_DEBUG "/MDd /Od /Zi /D_DEBUG /DDEBUG") + SET(CMAKE_CXX_FLAGS_RELEASE "/O1 /DNDEBUG") +endif () +#if(ENABLE_COVERAGE) +# # set compiler flags +# set(CMAKE_CXX_FLAGS "-O0 -coverage") +# +# # find required tools +# find_program(LCOV lcov REQUIRED) +# find_program(GENHTML genhtml REQUIRED) +# +# # add coverage target +# add_custom_target(coverage +# # gather data +# COMMAND ${LCOV} --directory . --exclude catch2 --exclude /usr/include --exclude test --capture --base-directory ${CMAKE_SOURCE_DIR} --output-file coverage.info +# # generate report +# COMMAND ${GENHTML} --demangle-cpp -o coverage coverage.info +# WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +#endif() -message(STATUS "Found SQLite3 ${SQLite3_VERSION}") -message(STATUS "Adding SQLite3 include directory: ${SQLite3_INCLUDE_DIRS}") -message(STATUS "Adding SQLite3 libs: ${SQLite3_LIBRARIES}") -message(STATUS "Adding MySQL include directory: ${MYSQL_INCLUDE_DIR}") -message(STATUS "Adding MySQL libs: ${MYSQL_LIBRARY}") - -message(STATUS "Adding PostgreSQL include directory: ${PostgreSQL_INCLUDE_DIR}") -message(STATUS "Adding PostgreSQL libs: ${PostgreSQL_LIBRARY}") - -message(STATUS "Common flags ${CMAKE_CXX_FLAGS}") -message(STATUS "Debug flags ${CMAKE_CXX_FLAGS_DEBUG}") -message(STATUS "Relase flags ${CMAKE_CXX_FLAGS_RELEASE}") - -message(STATUS "Linker flags ${CMAKE_EXE_LINKER_FLAGS}") - -enable_testing() - -add_subdirectory(src) -add_subdirectory(test) +add_subdirectory(source) add_subdirectory(backends) -add_subdirectory(demo) +add_subdirectory(test) diff --git a/Todo.md b/Todo.md deleted file mode 100644 index 6955f58..0000000 --- a/Todo.md +++ /dev/null @@ -1,47 +0,0 @@ -# Todo - -- Add is_valid() method to connection & connection_impl -- Read in entity fields -- Add special handling for update in backends -- Add ODBC/SQL Server backend - - -Fetch eager strategies -====================== - -ONE TO ONE/MANY -*person* *address* -- has one address - belongs to person - -=> join "address" on "person.id" == "address.person_id" - -*address* *person* -- belongs to person - has one address - -=> join "person" on "address.person_id" == "person.id" - -*book* *author* -- belongs to author - has many books - -- => join "author" on "book.author_id" == "author.id" - -HAS MANY TO ONE (WITHOUT RELATION TABLE) - -*author* *book* -- has many books - belongs to author - -- => join "book" on "author.id" == "book.author_id" - -if "has many" type has primary key & field "author_id" -if table name belongs to entity template type? - -HAS MANY TO MANY (WITHOUT RELATION TABLE) - -*student* *student_course* *course* -- has many courses - belongs to student - - belongs to course - has many students - -=> join "student_course" on "student.id" == "student_course.student_id" - join "student_course" on "course.id" == "student_course.course_id" - -if has many type hasn't primary key (is relation table) diff --git a/backends/CMakeLists.txt b/backends/CMakeLists.txt index cfdc8d1..353b8a2 100644 --- a/backends/CMakeLists.txt +++ b/backends/CMakeLists.txt @@ -1,3 +1,4 @@ -add_subdirectory(sqlite) +#add_subdirectory(sqlite) add_subdirectory(postgres) -add_subdirectory(mysql) \ No newline at end of file +#add_subdirectory(mysql) +#add_subdirectory(odbc) \ No newline at end of file diff --git a/backends/mysql/CMakeLists.txt b/backends/mysql/CMakeLists.txt deleted file mode 100644 index 57cf66c..0000000 --- a/backends/mysql/CMakeLists.txt +++ /dev/null @@ -1,37 +0,0 @@ -set(HEADER - include/mysql_connection.hpp - include/mysql_error.hpp - include/mysql_result_reader.hpp - include/mysql_dialect.hpp - include/mysql_statement.hpp - include/mysql_parameter_binder.hpp - include/mysql_prepared_result_reader.hpp -) - -set(SOURCES - src/mysql_connection.cpp - src/mysql_error.cpp - src/mysql_result_reader.cpp - src/mysql_dialect.cpp - src/mysql_statement.cpp - src/mysql_parameter_binder.cpp - src/mysql_prepared_result_reader.cpp -) - -set(LIBRARY_TARGET matador-mysql) - -add_subdirectory(test) - -add_library(${LIBRARY_TARGET} MODULE ${SOURCES} ${HEADER}) - -set_target_properties(${LIBRARY_TARGET} - PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/backends" -) - -target_include_directories(${LIBRARY_TARGET} PRIVATE - ${PROJECT_SOURCE_DIR}/include - ${PROJECT_SOURCE_DIR}/backends/mysql/include - ${MYSQL_INCLUDE_DIR}) - -target_link_libraries(${LIBRARY_TARGET} matador ${MYSQL_LIBRARY}) diff --git a/backends/mysql/include/mysql_connection.hpp b/backends/mysql/include/mysql_connection.hpp deleted file mode 100644 index c9ade35..0000000 --- a/backends/mysql/include/mysql_connection.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef QUERY_POSTGRES_CONNECTION_HPP -#define QUERY_POSTGRES_CONNECTION_HPP - -#ifdef _MSC_VER -#ifdef matador_mysql_EXPORTS -#define MATADOR_MYSQL_API __declspec(dllexport) -#else -#define MATADOR_MYSQL_API __declspec(dllimport) -#endif -#pragma warning(disable: 4355) -#else -#define MATADOR_MYSQL_API -#endif - -#include "matador/sql/connection_impl.hpp" - -#include - -#ifdef _MSC_VER -#include -#else -#include -#endif - -namespace matador::backends::mysql { - -class mysql_connection : public matador::sql::connection_impl -{ -public: - explicit mysql_connection(const sql::connection_info &info); - void open() override; - void close() override; - bool is_open() override; - - std::unique_ptr fetch(const std::string &stmt) override; - std::unique_ptr prepare(sql::query_context context) override; - - size_t execute(const std::string &stmt) override; - - std::vector describe(const std::string& table) override; - - bool exists(const std::string &schema_name, const std::string &table_name) override; - -private: - mutable std::unique_ptr mysql_; - - using string_to_int_map = std::unordered_map; - - static string_to_int_map statement_name_map_; -}; - -} - -extern "C" -{ -MATADOR_MYSQL_API matador::sql::connection_impl* create_database(const matador::sql::connection_info &info); - -MATADOR_MYSQL_API void destroy_database(matador::sql::connection_impl *db); -} - -#endif //QUERY_POSTGRES_CONNECTION_HPP diff --git a/backends/mysql/include/mysql_dialect.hpp b/backends/mysql/include/mysql_dialect.hpp deleted file mode 100644 index e2e3747..0000000 --- a/backends/mysql/include/mysql_dialect.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef QUERY_POSTGRES_DIALECT_HPP -#define QUERY_POSTGRES_DIALECT_HPP - -#ifdef _MSC_VER -#ifdef matador_mysql_EXPORTS -#define MATADOR_MYSQL_API __declspec(dllexport) -#else -#define MATADOR_MYSQL_API __declspec(dllimport) -#endif -#pragma warning(disable: 4355) -#else -#define MATADOR_MYSQL_API -#endif - -#include "matador/sql/dialect.hpp" - -extern "C" [[maybe_unused]] MATADOR_MYSQL_API const matador::sql::dialect* get_dialect(); - -#endif //QUERY_POSTGRES_DIALECT_HPP diff --git a/backends/mysql/include/mysql_error.hpp b/backends/mysql/include/mysql_error.hpp deleted file mode 100644 index 3f0ad4e..0000000 --- a/backends/mysql/include/mysql_error.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef QUERY_POSTGRES_ERROR_HPP -#define QUERY_POSTGRES_ERROR_HPP - -#ifdef _MSC_VER -#include -#else -#include -#endif - -#include - -namespace matador::backends::mysql { - -void throw_mysql_error(const char *what, const std::string &source); -void throw_mysql_error(MYSQL *db, const std::string &source); -void throw_mysql_error(MYSQL_STMT *stmt, const std::string &source, const std::string &sql); - -} - -#endif //QUERY_POSTGRES_ERROR_HPP diff --git a/backends/mysql/include/mysql_parameter_binder.hpp b/backends/mysql/include/mysql_parameter_binder.hpp deleted file mode 100644 index 74f2aaf..0000000 --- a/backends/mysql/include/mysql_parameter_binder.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef QUERY_POSTGRES_PARAMETER_BINDER_H -#define QUERY_POSTGRES_PARAMETER_BINDER_H - -#include "matador/sql/parameter_binder.hpp" - -#ifdef _MSC_VER -#include -#else -#include -#endif - -#include - -namespace matador::backends::mysql { - -struct mysql_result_info -{ - unsigned long length = 0; - my_bool is_null = false; - my_bool error = false; -// std::unique_ptr buffer; - char *buffer = nullptr; - unsigned long buffer_length = 0; - bool is_allocated = false; - - ~mysql_result_info() - { - if (is_allocated) { - delete [] buffer; - } - } -}; - -class mysql_parameter_binder final : public sql::parameter_binder -{ -public: - explicit mysql_parameter_binder(size_t size); - - void bind(size_t pos, char i) override; - void bind(size_t pos, short i) override; - void bind(size_t pos, int i) override; - void bind(size_t pos, long i) override; - void bind(size_t pos, long long int i) override; - void bind(size_t pos, unsigned char i) override; - void bind(size_t pos, unsigned short i) override; - void bind(size_t pos, unsigned int i) override; - void bind(size_t pos, unsigned long i) override; - void bind(size_t pos, unsigned long long int i) override; - void bind(size_t pos, bool b) override; - void bind(size_t pos, float d) override; - void bind(size_t pos, double d) override; - void bind(size_t pos, const char *string) override; - void bind(size_t pos, const char *string, size_t size) override; - void bind(size_t pos, const std::string &string) override; - void bind(size_t pos, const std::string &x, size_t size) override; - - void bind(size_t pos, const utils::blob &blob) override; - - [[nodiscard]] std::vector& bind_params(); - -private: - struct is_null_t - { - my_bool is_null = false; - }; - - std::vector bind_params_; - std::vector is_null_vector; - std::vector info_; -}; - -} - -#endif //QUERY_POSTGRES_PARAMETER_BINDER_H diff --git a/backends/mysql/include/mysql_prepared_result_reader.hpp b/backends/mysql/include/mysql_prepared_result_reader.hpp deleted file mode 100644 index 7135308..0000000 --- a/backends/mysql/include/mysql_prepared_result_reader.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef QUERY_MYSQL_PREPARED_RESULT_READER_HPP -#define QUERY_MYSQL_PREPARED_RESULT_READER_HPP - -#include "matador/sql/query_result_reader.hpp" - -#ifdef _MSC_VER -#include -#else -#include -#endif - -namespace matador::backends::mysql { - -class mysql_prepared_result_reader : public sql::query_result_reader -{ -public: - explicit mysql_prepared_result_reader(MYSQL_STMT *stmt); - ~mysql_prepared_result_reader() override; - - [[nodiscard]] size_t column_count() const override; - [[nodiscard]] const char *column(size_t index) const override; - bool fetch() override; - -private: - MYSQL_STMT *stmt_{}; - - MYSQL_ROW current_row_{}; - - size_t row_count_{}; - size_t column_count_{}; - int row_index_{-1}; -}; - -} - -#endif //QUERY_MYSQL_PREPARED_RESULT_READER_HPP diff --git a/backends/mysql/include/mysql_result_reader.hpp b/backends/mysql/include/mysql_result_reader.hpp deleted file mode 100644 index f1fb756..0000000 --- a/backends/mysql/include/mysql_result_reader.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef QUERY_POSTGRES_RESULT_READER_HPP -#define QUERY_POSTGRES_RESULT_READER_HPP - -#include "matador/sql/query_result_reader.hpp" - -#ifdef _MSC_VER -#include -#else -#include -#endif - -namespace matador::backends::mysql { - -class mysql_result_reader : public sql::query_result_reader -{ -public: - explicit mysql_result_reader(MYSQL_RES *result, unsigned int column_count); - ~mysql_result_reader() override; - - [[nodiscard]] size_t column_count() const override; - [[nodiscard]] const char *column(size_t index) const override; - bool fetch() override; - -private: - MYSQL_RES *result_{}; - - MYSQL_ROW current_row_{}; - - size_t row_count_{}; - size_t column_count_{}; - int row_index_{-1}; -}; - -} - -#endif //QUERY_POSTGRES_RESULT_READER_HPP diff --git a/backends/mysql/include/mysql_statement.hpp b/backends/mysql/include/mysql_statement.hpp deleted file mode 100644 index 5b936d9..0000000 --- a/backends/mysql/include/mysql_statement.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef QUERY_POSTGRES_STATEMENT_HPP -#define QUERY_POSTGRES_STATEMENT_HPP - -#include "matador/sql/statement_impl.hpp" - -#include "mysql_parameter_binder.hpp" - -#ifdef _MSC_VER -#include -#else -#include -#endif - -namespace matador::backends::mysql { - -class mysql_statement final : public sql::statement_impl -{ -public: - mysql_statement(MYSQL_STMT *stmt, const sql::query_context &query); - - size_t execute() override; - std::unique_ptr fetch() override; - void reset() override; -protected: - sql::parameter_binder& binder() override; - -private: - MYSQL_STMT *stmt_{nullptr}; - - std::string name_; - - mysql_parameter_binder binder_; -}; - -} - -#endif //QUERY_POSTGRES_STATEMENT_HPP diff --git a/backends/mysql/src/mysql_connection.cpp b/backends/mysql/src/mysql_connection.cpp deleted file mode 100644 index 8bc5a1c..0000000 --- a/backends/mysql/src/mysql_connection.cpp +++ /dev/null @@ -1,280 +0,0 @@ -#include "mysql_connection.hpp" -#include "mysql_error.hpp" -#include "mysql_result_reader.hpp" -#include "mysql_statement.hpp" - -#include "matador/sql/record.hpp" - -#include -#include - -namespace matador::backends::mysql { - -mysql_connection::string_to_int_map mysql_connection::statement_name_map_{}; - -mysql_connection::mysql_connection(const sql::connection_info &info) -: connection_impl(info) {} - -void mysql_connection::open() -{ - if (is_open()) { - return; - } - - mysql_ = std::make_unique(); - - if (!mysql_init(mysql_.get())) { - throw_mysql_error(mysql_.get(), "mysql_init"); - } - - if (!mysql_real_connect(mysql_.get(), - info().hostname.c_str(), - info().user.c_str(), - !info().password.empty() ? info().password.c_str() : nullptr, - info().database.c_str(), - info().port, - nullptr, - 0)) { - // disconnect all handles - const std::string error_message = mysql_error(mysql_.get()); - mysql_close(mysql_.get()); - - mysql_.reset(); - // throw exception - throw_mysql_error(error_message.c_str(), "mysql_real_connect"); - } -} - -void mysql_connection::close() -{ - if (mysql_) { - mysql_close(mysql_.get()); - mysql_.reset(); - } -} - -bool mysql_connection::is_open() -{ - return mysql_ != nullptr; -} - -sql::data_type_t to_type(enum_field_types type, unsigned int flags) -{ - switch (type) { - case MYSQL_TYPE_TINY: - return flags & UNSIGNED_FLAG ? sql::data_type_t::type_unsigned_char : sql::data_type_t::type_char; - case MYSQL_TYPE_SHORT: - return flags & UNSIGNED_FLAG ? sql::data_type_t::type_unsigned_short : sql::data_type_t::type_short; - case MYSQL_TYPE_LONG: - return flags & UNSIGNED_FLAG ? sql::data_type_t::type_unsigned_int : sql::data_type_t::type_int; - case MYSQL_TYPE_LONGLONG: - return flags & UNSIGNED_FLAG ? sql::data_type_t::type_unsigned_long_long : sql::data_type_t::type_long_long; - case MYSQL_TYPE_FLOAT: - return sql::data_type_t::type_float; - case MYSQL_TYPE_DOUBLE: - return sql::data_type_t::type_double; - case MYSQL_TYPE_VARCHAR: - case MYSQL_TYPE_VAR_STRING: - return sql::data_type_t::type_varchar; - case MYSQL_TYPE_BLOB: - return sql::data_type_t::type_blob; - case MYSQL_TYPE_STRING: - return sql::data_type_t::type_text; - case MYSQL_TYPE_DATE: - return sql::data_type_t::type_date; - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIMESTAMP: - return sql::data_type_t::type_time; - default: - return sql::data_type_t::type_unknown; - } -} - -utils::constraints to_constraints(unsigned int flags) -{ - utils::constraints options{utils::constraints::NONE}; - if (flags & PRI_KEY_FLAG) { - options |= utils::constraints::PRIMARY_KEY; - } - if (flags & UNIQUE_KEY_FLAG) { - options |= utils::constraints::UNIQUE; - } - - return options; -} - -sql::null_option to_null_option(unsigned int flags) -{ - return flags & NOT_NULL_FLAG ? sql::null_option::NOT_NULL : sql::null_option::NULLABLE; -} - -sql::data_type_t string2type(const std::string &type_string) -{ - if (strncmp(type_string.c_str(), "tinyint", 7) == 0) { - return sql::data_type_t::type_char; - } else if (strncmp(type_string.c_str(), "smallint", 8) == 0) { - if (strstr(type_string.c_str(), "unsigned") != nullptr) { - return sql::data_type_t::type_unsigned_short; - } else { - return sql::data_type_t::type_short; - } - } else if (strncmp(type_string.c_str(), "int", 3) == 0) { - if (strstr(type_string.c_str(), "unsigned") != nullptr) { - return sql::data_type_t::type_unsigned_int; - } else { - return sql::data_type_t::type_int; - } - } else if (strncmp(type_string.c_str(), "bigint", 6) == 0) { - if (strstr(type_string.c_str(), "unsigned") != nullptr) { - return sql::data_type_t::type_unsigned_long_long; - } else { - return sql::data_type_t::type_long_long; - } - } else if (strcmp(type_string.c_str(), "date") == 0) { - return sql::data_type_t::type_date; - } else if (strncmp(type_string.c_str(), "datetime", 8) == 0) { - return sql::data_type_t::type_time; - } else if (strcmp(type_string.c_str(), "float") == 0) { - return sql::data_type_t::type_float; - } else if (strcmp(type_string.c_str(), "double") == 0) { - return sql::data_type_t::type_double; - } else if (strncmp(type_string.c_str(), "varchar", 7) == 0) { - return sql::data_type_t::type_varchar; - } else if (strncmp(type_string.c_str(), "text", 4) == 0) { - return sql::data_type_t::type_text; - } else { - return sql::data_type_t::type_unknown; - } -} - -struct type_info -{ - sql::data_type_t type{sql::data_type_t::type_unknown}; - size_t size{}; -}; - -type_info determine_type_info(const std::string &type_string) -{ - static const std::regex TYPE_REGEX(R"(^(\w+)(\((\d+)(,(\d+))?\))?$)"); - std::smatch matcher; - - type_info result; - if (std::regex_match(type_string, matcher, TYPE_REGEX)) { - result.type = string2type(matcher[1].str()); - if (matcher[3].matched) { - result.size = std::stoi(matcher[3].str()); - } - } - return result; -} - -std::unique_ptr mysql_connection::fetch(const std::string &stmt) -{ - if (mysql_query(mysql_.get(), stmt.c_str())) { - throw_mysql_error(mysql_.get(), stmt); - } - - auto result = mysql_store_result(mysql_.get()); - if (result == nullptr) { - throw_mysql_error(mysql_.get(), stmt); - } - - auto field_count = mysql_num_fields(result); - auto fields = mysql_fetch_fields(result); - std::vector prototype; - for (unsigned i = 0; i < field_count; ++i) { - auto type = to_type(fields[i].type, fields[i].flags); - auto options = to_constraints(fields[i].flags); - auto null_opt = to_null_option(fields[i].flags); - - prototype.emplace_back(fields[i].name, type, options, null_opt); - } - - return std::move(std::make_unique(std::make_unique(result, field_count), std::move(prototype))); -} - -std::unique_ptr mysql_connection::prepare(sql::query_context context) -{ - MYSQL_STMT *stmt = mysql_stmt_init(mysql_.get()); - if (stmt == nullptr) { - throw_mysql_error(mysql_.get(), "mysql_stmt_init"); - } - - if (mysql_stmt_prepare(stmt, context.sql.c_str(), static_cast(context.sql.size())) != 0) { - throw_mysql_error(stmt, "mysql_stmt_prepare", context.sql); - } - - return std::make_unique(stmt, std::move(context)); -} - -size_t mysql_connection::execute(const std::string &stmt) -{ - if (mysql_query(mysql_.get(), stmt.c_str())) { - throw_mysql_error(mysql_.get(), stmt); - } - - return mysql_affected_rows(mysql_.get()); -} - -std::vector mysql_connection::describe(const std::string &table) -{ - std::string stmt("SHOW COLUMNS FROM " + table); - - if (mysql_query(mysql_.get(), stmt.c_str())) { - throw_mysql_error(mysql_.get(), stmt); - } - - auto result = mysql_store_result(mysql_.get()); - if (result == nullptr) { - throw_mysql_error(mysql_.get(), stmt); - } - - mysql_result_reader reader(result, mysql_num_fields(result)); - std::vector prototype; - while (reader.fetch()) { - - char *end = nullptr; - std::string name = reader.column(0); - - auto typeinfo = determine_type_info(reader.column(1)); - end = nullptr; - sql::null_option null_opt{sql::null_option::NULLABLE}; - if (strtoul(reader.column(2), &end, 10) == 0) { - null_opt = sql::null_option::NOT_NULL; - } - prototype.push_back({name, typeinfo.type, {typeinfo.size}, null_opt, prototype.size()}); - } - - return prototype; -} - -bool mysql_connection::exists(const std::string &/*schema_name*/, const std::string &table_name) -{ - std::string stmt("SELECT 1 FROM information_schema.tables WHERE table_schema = '" + info().database + "' AND table_name = '" + table_name + "'"); - - if (mysql_query(mysql_.get(), stmt.c_str())) { - throw_mysql_error(mysql_.get(), stmt); - } - - auto result = mysql_store_result(mysql_.get()); - if (result == nullptr) { - throw_mysql_error(mysql_.get(), stmt); - } - - return result->row_count == 1; -} - -} - -extern "C" -{ -MATADOR_MYSQL_API matador::sql::connection_impl *create_database(const matador::sql::connection_info &info) -{ - return new matador::backends::mysql::mysql_connection(info); -} - -MATADOR_MYSQL_API void destroy_database(matador::sql::connection_impl *db) -{ - delete db; -} -} diff --git a/backends/mysql/src/mysql_dialect.cpp b/backends/mysql/src/mysql_dialect.cpp deleted file mode 100644 index 5ab4f49..0000000 --- a/backends/mysql/src/mysql_dialect.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "mysql_dialect.hpp" - -#include "matador/sql/dialect_builder.hpp" - -[[maybe_unused]] const matador::sql::dialect *get_dialect() -{ - using namespace matador::sql; - const static dialect d = dialect_builder::builder() - .create() - .with_token_replace_map({ - {dialect::token_t::START_QUOTE, "`"}, - {dialect::token_t::END_QUOTE, "`"}, - }) - .with_default_schema_name("") - .build(); - return &d; -} diff --git a/backends/mysql/src/mysql_error.cpp b/backends/mysql/src/mysql_error.cpp deleted file mode 100644 index ea8d503..0000000 --- a/backends/mysql/src/mysql_error.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "mysql_error.hpp" - -#include - -namespace matador::backends::mysql { - -void throw_mysql_error(const char *what, const std::string &source) -{ - std::stringstream msg; - msg << "mysql error (" << source << "): " << what; - throw std::logic_error(msg.str()); -} - -void throw_mysql_error(MYSQL *db, const std::string &source) -{ - if (mysql_errno(db) != 0) { - throw_mysql_error(mysql_error(db), source); - } -} - -void throw_mysql_error(MYSQL_STMT *stmt, const std::string &source, const std::string &sql) -{ - if (mysql_stmt_errno(stmt) != 0) { - std::stringstream msg; - msg << "mysql error (" << source << ") " << mysql_stmt_error(stmt) << ": " << sql; - throw std::logic_error(msg.str()); - } -} - -} \ No newline at end of file diff --git a/backends/mysql/src/mysql_parameter_binder.cpp b/backends/mysql/src/mysql_parameter_binder.cpp deleted file mode 100644 index 213f08d..0000000 --- a/backends/mysql/src/mysql_parameter_binder.cpp +++ /dev/null @@ -1,192 +0,0 @@ -#include "mysql_parameter_binder.hpp" - -namespace matador::backends::mysql { - -namespace detail { - -template < class T > -void bind_value(enum_field_types type, T value, MYSQL_BIND &bind, my_bool &is_null) -{ - if (bind.buffer == nullptr) { - // allocating memory - bind.buffer = new char[sizeof(T)]; - bind.buffer_type = type; - bind.buffer_length = sizeof(T); - bind.is_null = &is_null; - bind.is_unsigned = std::is_unsigned::value; - } - *static_cast(bind.buffer) = value; - is_null = false; -} - -void bind_value(enum_field_types type, const char *value, size_t, MYSQL_BIND &bind, my_bool &is_null) -{ - std::size_t len(strlen(value) + 1); - if (bind.buffer_length < len) { - // reallocate memory - delete [] static_cast(bind.buffer); - bind.buffer = nullptr; - bind.buffer_length = 0; - bind.buffer_type = type; - bind.is_null = &is_null; - } - if (bind.buffer == nullptr) { - // allocating memory - bind.buffer = new char[len]; - memset(bind.buffer, 0, len); - } - bind.buffer_length = (unsigned long)(len - 1); -#ifdef _MSC_VER - strncpy_s(static_cast(bind.buffer), len, value, _TRUNCATE); -#else - strncpy(static_cast(bind.buffer), value, len); -#endif - is_null = false; -} - -//void bind_value(enum_field_types type, const matador::date &x, MYSQL_BIND &bind, my_bool &is_null) -//{ -// if (bind.buffer == nullptr) { -// size_t s = sizeof(MYSQL_TIME); -// bind.buffer = new char[s]; -// bind.buffer_length = (unsigned long)s; -// bind.is_null = &is_null; -// bind.buffer_type = type; -// bind.length = nullptr; -// } -// memset(bind.buffer, 0, sizeof(MYSQL_TIME)); -// is_null = false; -// auto *mt = static_cast(bind.buffer); -// mt->day = (unsigned int)x.day(); -// mt->month = (unsigned int)x.month(); -// mt->year = (unsigned int)x.year(); -// mt->time_type = MYSQL_TIMESTAMP_DATE; -//} -// -//void bind_value(enum_field_types type, const matador::time &x, MYSQL_BIND &bind, my_bool &is_null) -//{ -// if (bind.buffer == nullptr) { -// size_t s = sizeof(MYSQL_TIME); -// bind.buffer = new char[s]; -// bind.buffer_length = (unsigned long)s; -// bind.buffer_type = type; -// bind.length = nullptr; -// bind.is_null = &is_null; -// } -// memset(bind.buffer, 0, sizeof(MYSQL_TIME)); -// is_null = false; -// auto *mt = static_cast(bind.buffer); -// mt->day = (unsigned int)x.day(); -// mt->month = (unsigned int)x.month(); -// mt->year = (unsigned int)x.year(); -// mt->hour = (unsigned int)x.hour(); -// mt->minute = (unsigned int)x.minute(); -// mt->second = (unsigned int)x.second(); -// mt->second_part = (unsigned long)x.milli_second() * 1000; -// mt->time_type = MYSQL_TIMESTAMP_DATETIME; -//} - -} - -mysql_parameter_binder::mysql_parameter_binder(size_t size) -: bind_params_(size) -, is_null_vector(size) -, info_(size) -{} - -void mysql_parameter_binder::bind(size_t pos, char i) -{ - detail::bind_value(MYSQL_TYPE_TINY, i, bind_params_[pos], is_null_vector[pos].is_null); -} - -void mysql_parameter_binder::bind(size_t pos, short i) -{ - detail::bind_value(MYSQL_TYPE_SHORT, i, bind_params_[pos], is_null_vector[pos].is_null); -} - -void mysql_parameter_binder::bind(size_t pos, int i) -{ - detail::bind_value(MYSQL_TYPE_LONG, i, bind_params_[pos], is_null_vector[pos].is_null); -} - -void mysql_parameter_binder::bind(size_t pos, long i) -{ - detail::bind_value(MYSQL_TYPE_LONG, i, bind_params_[pos], is_null_vector[pos].is_null); -} - -void mysql_parameter_binder::bind(size_t pos, long long int i) -{ - detail::bind_value(MYSQL_TYPE_LONGLONG, i, bind_params_[pos], is_null_vector[pos].is_null); -} - -void mysql_parameter_binder::bind(size_t pos, unsigned char i) -{ - detail::bind_value(MYSQL_TYPE_TINY, i, bind_params_[pos], is_null_vector[pos].is_null); -} - -void mysql_parameter_binder::bind(size_t pos, unsigned short i) -{ - detail::bind_value(MYSQL_TYPE_SHORT, i, bind_params_[pos], is_null_vector[pos].is_null); -} - -void mysql_parameter_binder::bind(size_t pos, unsigned int i) -{ - detail::bind_value(MYSQL_TYPE_LONG, i, bind_params_[pos], is_null_vector[pos].is_null); -} - -void mysql_parameter_binder::bind(size_t pos, unsigned long i) -{ - detail::bind_value(MYSQL_TYPE_LONG, i, bind_params_[pos], is_null_vector[pos].is_null); -} - -void mysql_parameter_binder::bind(size_t pos, unsigned long long int i) -{ - detail::bind_value(MYSQL_TYPE_LONGLONG, i, bind_params_[pos], is_null_vector[pos].is_null); -} - -void mysql_parameter_binder::bind(size_t pos, bool b) -{ - detail::bind_value(MYSQL_TYPE_TINY, b, bind_params_[pos], is_null_vector[pos].is_null); -} - -void mysql_parameter_binder::bind(size_t pos, float d) -{ - detail::bind_value(MYSQL_TYPE_FLOAT, d, bind_params_[pos], is_null_vector[pos].is_null); -} - -void mysql_parameter_binder::bind(size_t pos, double d) -{ - detail::bind_value(MYSQL_TYPE_DOUBLE, d, bind_params_[pos], is_null_vector[pos].is_null); -} - -void mysql_parameter_binder::bind(size_t pos, const char *str) -{ - detail::bind_value(MYSQL_TYPE_STRING, str, strlen(str), bind_params_[pos], is_null_vector[pos].is_null); -} - -void mysql_parameter_binder::bind(size_t pos, const char *str, size_t size) -{ - detail::bind_value(MYSQL_TYPE_VAR_STRING, str, size, bind_params_[pos], is_null_vector[pos].is_null); -} - -void mysql_parameter_binder::bind(size_t pos, const std::string &str) -{ - detail::bind_value(MYSQL_TYPE_STRING, str.data(), str.size(), bind_params_[pos], is_null_vector[pos].is_null); -} - -void mysql_parameter_binder::bind(size_t pos, const std::string &str, size_t size) -{ - detail::bind_value(MYSQL_TYPE_VAR_STRING, str.data(), size, bind_params_[pos], is_null_vector[pos].is_null); -} - -void mysql_parameter_binder::bind(size_t pos, const utils::blob &blob) -{ - -} - -std::vector &mysql_parameter_binder::bind_params() -{ - return bind_params_; -} - -} \ No newline at end of file diff --git a/backends/mysql/src/mysql_prepared_result_reader.cpp b/backends/mysql/src/mysql_prepared_result_reader.cpp deleted file mode 100644 index e524248..0000000 --- a/backends/mysql/src/mysql_prepared_result_reader.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "mysql_prepared_result_reader.hpp" - -namespace matador::backends::mysql { - -mysql_prepared_result_reader::mysql_prepared_result_reader(MYSQL_STMT *stmt) -: stmt_(stmt) -{} - -mysql_prepared_result_reader::~mysql_prepared_result_reader() -{ - -} - -size_t mysql_prepared_result_reader::column_count() const -{ - return 0; -} - -const char *mysql_prepared_result_reader::column(size_t index) const -{ - return nullptr; -} - -bool mysql_prepared_result_reader::fetch() -{ - return false; -} -} \ No newline at end of file diff --git a/backends/mysql/src/mysql_result_reader.cpp b/backends/mysql/src/mysql_result_reader.cpp deleted file mode 100644 index 828ebf7..0000000 --- a/backends/mysql/src/mysql_result_reader.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "mysql_result_reader.hpp" - -namespace matador::backends::mysql { - -mysql_result_reader::mysql_result_reader(MYSQL_RES *result, unsigned int column_count) -: result_(result) -, row_count_(mysql_num_rows(result_)) -, column_count_(column_count) -{} - -mysql_result_reader::~mysql_result_reader() -{ - if (result_) { - mysql_free_result(result_); - } -} - -size_t mysql_result_reader::column_count() const -{ - return column_count_; -} - -const char *mysql_result_reader::column(size_t index) const -{ - return current_row_[index]; -} - -bool mysql_result_reader::fetch() -{ - current_row_ = mysql_fetch_row(result_); - - return current_row_ != nullptr; -} - -} \ No newline at end of file diff --git a/backends/mysql/src/mysql_statement.cpp b/backends/mysql/src/mysql_statement.cpp deleted file mode 100644 index f8c2761..0000000 --- a/backends/mysql/src/mysql_statement.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "mysql_statement.hpp" -#include "mysql_error.hpp" -#include "mysql_prepared_result_reader.hpp" - -namespace matador::backends::mysql { - -mysql_statement::mysql_statement(MYSQL_STMT *stmt, const sql::query_context &query) -: statement_impl(query) -, stmt_(stmt) -, binder_(query_.bind_vars.size()) -{} - -size_t mysql_statement::execute() -{ - if (!binder_.bind_params().empty()) { - if (mysql_stmt_bind_param(stmt_, binder_.bind_params().data()) != 0) { - throw_mysql_error(stmt_, "mysql", query_.sql); - } - } - - if (mysql_stmt_execute(stmt_) != 0) { - throw_mysql_error(stmt_, "mysql", query_.sql); - } - - return mysql_stmt_affected_rows(stmt_); -} - -std::unique_ptr mysql_statement::fetch() -{ - if (!binder_.bind_params().empty()) { - if (mysql_stmt_bind_param(stmt_, binder_.bind_params().data()) != 0) { - throw_mysql_error(stmt_, "mysql", query_.sql); - } - } - - if (mysql_stmt_execute(stmt_) != 0) { - throw_mysql_error(stmt_, "mysql", query_.sql); - } - if (mysql_stmt_store_result(stmt_) != 0) { - throw_mysql_error(stmt_, "mysql", query_.sql); - } - - return std::move(std::make_unique(std::make_unique(stmt_), std::move(query_.prototype))); -} - -void mysql_statement::reset() {} - -sql::parameter_binder& mysql_statement::binder() -{ - return binder_; -} - -} \ No newline at end of file diff --git a/backends/mysql/test/CMakeLists.txt b/backends/mysql/test/CMakeLists.txt deleted file mode 100644 index e867785..0000000 --- a/backends/mysql/test/CMakeLists.txt +++ /dev/null @@ -1,46 +0,0 @@ -Include(FetchContent) - -FetchContent_Declare( - Catch2 - GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v3.5.4 # or a later release -) - -FetchContent_MakeAvailable(Catch2) - -list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) -include(CTest) -include(Catch) - -set(MYSQL_CONNECTION_STRING "mysql://test:test123!@127.0.0.1:3306/matador_test") - -configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/mysql/test/connection.hpp @ONLY IMMEDIATE) - -message(STATUS "mysql connection string: ${MYSQL_CONNECTION_STRING}") - -set(TEST_SOURCES - ../../tests/QueryTest.cpp - ../../tests/QueryTest.cpp - ../../tests/ConnectionTest.cpp - ../../tests/QueryRecordTest.cpp - ../../tests/StatementTest.cpp - ../../tests/TypeTraitsTest.cpp - ../../tests/StatementCacheTest.cpp - ../../tests/SessionTest.cpp) - -set(LIBRARY_TEST_TARGET mysql_tests) - -add_executable(${LIBRARY_TEST_TARGET} ${TEST_SOURCES}) - -target_link_libraries(${LIBRARY_TEST_TARGET} PRIVATE - Catch2::Catch2WithMain - matador - ${CMAKE_DL_LIBS} - ${MySQL_LIBRARY}) - -target_include_directories(${LIBRARY_TEST_TARGET} - PUBLIC $/include - PRIVATE $/test - PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) - -catch_discover_tests(${LIBRARY_TEST_TARGET} TEST_SUFFIX " (MySQL)") diff --git a/backends/mysql/test/Connection.hpp.in b/backends/mysql/test/Connection.hpp.in deleted file mode 100644 index 1d952f6..0000000 --- a/backends/mysql/test/Connection.hpp.in +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef MYSQL_CONNECTION_HPP -#define MYSQL_CONNECTION_HPP - -namespace matador::test::connection { - const char* const dns = "@MYSQL_CONNECTION_STRING@"; -} - -#endif /*SQLITE_CONNECTION_HPP*/ \ No newline at end of file diff --git a/backends/postgres/CMakeLists.txt b/backends/postgres/CMakeLists.txt index 5b428c0..aa5da49 100644 --- a/backends/postgres/CMakeLists.txt +++ b/backends/postgres/CMakeLists.txt @@ -18,7 +18,7 @@ set(SOURCES set(LIBRARY_TARGET matador-postgres) -add_subdirectory(test) +#add_subdirectory(test) add_library(${LIBRARY_TARGET} MODULE ${SOURCES} ${HEADER}) @@ -32,4 +32,10 @@ target_include_directories(${LIBRARY_TARGET} PRIVATE ${PROJECT_SOURCE_DIR}/backends/postgres/include ${PostgreSQL_INCLUDE_DIRS}) -target_link_libraries(${LIBRARY_TARGET} matador ${PostgreSQL_LIBRARIES}) +target_link_libraries(${LIBRARY_TARGET} + matador-core + matador-orm + ${CMAKE_DL_LIBS} + ${CMAKE_THREAD_LIBS_INIT} + ${PostgreSQL_LIBRARIES} +) diff --git a/backends/postgres/include/postgres_connection.hpp b/backends/postgres/include/postgres_connection.hpp index 3b27ff9..43da962 100644 --- a/backends/postgres/include/postgres_connection.hpp +++ b/backends/postgres/include/postgres_connection.hpp @@ -12,7 +12,7 @@ #define MATADOR_POSTGRES_API #endif -#include "matador/sql/connection_impl.hpp" +#include "matador/sql/interface/connection_impl.hpp" #include @@ -20,22 +20,25 @@ namespace matador::backends::postgres { -class postgres_connection : public matador::sql::connection_impl +class postgres_connection : public sql::connection_impl { public: explicit postgres_connection(const sql::connection_info &info); - void open() override; - void close() override; - bool is_open() override; + utils::result open() override; + utils::result close() override; + [[nodiscard]] utils::result is_open() const override; + [[nodiscard]] utils::result is_valid() const override; + [[nodiscard]] utils::result client_version() const override; + [[nodiscard]] utils::result server_version() const override; - std::unique_ptr fetch(const std::string &stmt) override; - std::unique_ptr prepare(sql::query_context context) override; + utils::result execute(const std::string &stmt) override; + utils::result, utils::error> prepare(const sql::query_context &context) override; + utils::result, utils::error> fetch(const sql::query_context &context) override; - size_t execute(const std::string &stmt) override; + utils::result, utils::error> describe(const std::string& table) override; + utils::result exists(const std::string &schema_name, const std::string &table_name) override; - std::vector describe(const std::string& table) override; - - bool exists(const std::string &schema_name, const std::string &table_name) override; + [[nodiscard]] std::string to_escaped_string( const utils::blob& value ) const override; private: [[nodiscard]] static std::string generate_statement_name(const sql::query_context &query) ; diff --git a/backends/postgres/include/postgres_error.hpp b/backends/postgres/include/postgres_error.hpp index 7351617..769b222 100644 --- a/backends/postgres/include/postgres_error.hpp +++ b/backends/postgres/include/postgres_error.hpp @@ -1,15 +1,27 @@ #ifndef QUERY_POSTGRES_ERROR_HPP #define QUERY_POSTGRES_ERROR_HPP +#include "matador/utils/error.hpp" + +#include "matador/sql/error_code.hpp" + #include #include namespace matador::backends::postgres { -void throw_postgres_error(const char *what, const std::string &source); -void throw_postgres_error(PGconn *db, const std::string &source); -void throw_postgres_error(PGresult *res, PGconn *db, const std::string &source, const std::string &sql); +utils::error make_error(sql::error_code ec, + const PGresult *res, + const PGconn *db, + const std::string &msg, + const std::string &sql = {}); + +bool is_result_error(const PGresult *res); + +// void throw_postgres_error(const char *what, const std::string &source); +// void throw_postgres_error(PGconn *db, const std::string &source); +// void throw_postgres_error(PGresult *res, PGconn *db, const std::string &source, const std::string &sql); } diff --git a/backends/postgres/include/postgres_parameter_binder.h b/backends/postgres/include/postgres_parameter_binder.h index fefebb7..2903a01 100644 --- a/backends/postgres/include/postgres_parameter_binder.h +++ b/backends/postgres/include/postgres_parameter_binder.h @@ -1,42 +1,52 @@ #ifndef QUERY_POSTGRES_PARAMETER_BINDER_H #define QUERY_POSTGRES_PARAMETER_BINDER_H -#include "matador/sql/parameter_binder.hpp" +#include "matador/utils/attribute_writer.hpp" #include namespace matador::backends::postgres { -class postgres_parameter_binder final : public sql::parameter_binder +class postgres_parameter_binder final : public utils::attribute_writer { public: + struct bind_data { + explicit bind_data(size_t size); + std::vector strings; + std::vector> bytes; + std::vector values; + std::vector lengths; + std::vector formats; + }; + explicit postgres_parameter_binder(size_t size); - void bind(size_t pos, char i) override; - void bind(size_t pos, short i) override; - void bind(size_t pos, int i) override; - void bind(size_t pos, long i) override; - void bind(size_t pos, long long int i) override; - void bind(size_t pos, unsigned char i) override; - void bind(size_t pos, unsigned short i) override; - void bind(size_t pos, unsigned int i) override; - void bind(size_t pos, unsigned long i) override; - void bind(size_t pos, unsigned long long int i) override; - void bind(size_t pos, bool b) override; - void bind(size_t pos, float d) override; - void bind(size_t pos, double d) override; - void bind(size_t pos, const char *string) override; - void bind(size_t pos, const char *string, size_t size) override; - void bind(size_t pos, const std::string &string) override; - void bind(size_t pos, const std::string &x, size_t size) override; + void write_value(size_t pos, const uint8_t &x) override; + void write_value(size_t pos, const uint16_t &x) override; + void write_value(size_t pos, const uint32_t &x) override; + void write_value(size_t pos, const uint64_t &x) override; + void write_value(size_t pos, const int8_t &x) override; + void write_value(size_t pos, const int16_t &x) override; + void write_value(size_t pos, const int32_t &x) override; + void write_value(size_t pos, const int64_t &x) override; + void write_value(size_t pos, const bool &x) override; + void write_value(size_t pos, const float &x) override; + void write_value(size_t pos, const double &x) override; + void write_value(size_t pos, const time &x ) override; + void write_value(size_t pos, const date &x ) override; + void write_value(size_t pos, const char *x) override; + void write_value(size_t pos, const char *x, size_t size) override; + void write_value(size_t pos, const std::string &x) override; + void write_value(size_t pos, const std::string &x, size_t size) override; + void write_value(size_t pos, const utils::blob &x) override; + void write_value(size_t pos, const utils::value &x, size_t size) override; - void bind(size_t pos, const utils::blob &blob) override; - - [[nodiscard]] const std::vector& params() const; + [[nodiscard]] const bind_data& params() const; private: - std::vector strings_; - std::vector params_; + bind_data bind_data_; +// std::vector strings_; +// std::vector params_; }; } diff --git a/backends/postgres/include/postgres_result_reader.hpp b/backends/postgres/include/postgres_result_reader.hpp index 84f7e0b..631d445 100644 --- a/backends/postgres/include/postgres_result_reader.hpp +++ b/backends/postgres/include/postgres_result_reader.hpp @@ -1,12 +1,40 @@ #ifndef QUERY_POSTGRES_RESULT_READER_HPP #define QUERY_POSTGRES_RESULT_READER_HPP +#include "matador/sql/interface/query_result_reader.hpp" + #include -#include "matador/sql/query_result_reader.hpp" namespace matador::backends::postgres { -class postgres_result_reader : public sql::query_result_reader +namespace detail { + +class empty_binder final : public utils::attribute_reader +{ +public: + void read_value(const char *, size_t, int8_t &) override {} + void read_value(const char *, size_t, int16_t &) override {} + void read_value(const char *, size_t, int32_t &) override {} + void read_value(const char *, size_t, int64_t &) override {} + void read_value(const char *, size_t, uint8_t &) override {} + void read_value(const char *, size_t, uint16_t &) override {} + void read_value(const char *, size_t, uint32_t &) override {} + void read_value(const char *, size_t, uint64_t &) override {} + void read_value(const char *, size_t, bool &) override {} + void read_value(const char *, size_t, float &) override {} + void read_value(const char *, size_t, double &) override {} + void read_value(const char *, size_t, time &) override {} + void read_value(const char *, size_t, date &) override {} + void read_value(const char *, size_t, char *, size_t) override {} + void read_value(const char *, size_t, std::string &) override {} + void read_value(const char *, size_t, std::string &, size_t) override {} + void read_value(const char *, size_t, utils::blob &) override {} + void read_value(const char *, size_t, utils::value &, size_t) override {} +}; + +} + +class postgres_result_reader final : public sql::query_result_reader { public: explicit postgres_result_reader(PGresult *result); @@ -14,7 +42,30 @@ public: [[nodiscard]] size_t column_count() const override; [[nodiscard]] const char *column(size_t index) const override; - bool fetch() override; + utils::result fetch() override; + [[nodiscard]] size_t start_column_index() const override; + + void read_value(const char *id, size_t index, int8_t &value) override; + void read_value(const char *id, size_t index, int16_t &value) override; + void read_value(const char *id, size_t index, int32_t &value) override; + void read_value(const char *id, size_t index, int64_t &value) override; + void read_value(const char *id, size_t index, uint8_t &value) override; + void read_value(const char *id, size_t index, uint16_t &value) override; + void read_value(const char *id, size_t index, uint32_t &value) override; + void read_value(const char *id, size_t index, uint64_t &value) override; + void read_value(const char *id, size_t index, bool &value) override; + void read_value(const char *id, size_t index, float &value) override; + void read_value(const char *id, size_t index, double &value) override; + void read_value(const char *id, size_t index, matador::time &value) override; + void read_value(const char *id, size_t index, matador::date &value) override; + void read_value(const char *id, size_t index, char *value, size_t size) override; + void read_value(const char *id, size_t index, std::string &value) override; + void read_value(const char *id, size_t index, std::string &value, size_t size) override; + void read_value(const char *id, size_t index, utils::blob &value) override; + void read_value(const char *id, size_t index, utils::value &val, size_t size) override; + +protected: + attribute_reader &result_binder() override; private: PGresult *result_{}; @@ -22,6 +73,8 @@ private: size_t row_count_{}; size_t column_count_{}; int row_index_{-1}; + + detail::empty_binder empty_binder_; }; } diff --git a/backends/postgres/include/postgres_statement.hpp b/backends/postgres/include/postgres_statement.hpp index 7c88762..ad4ced5 100644 --- a/backends/postgres/include/postgres_statement.hpp +++ b/backends/postgres/include/postgres_statement.hpp @@ -1,7 +1,7 @@ #ifndef QUERY_POSTGRES_STATEMENT_HPP #define QUERY_POSTGRES_STATEMENT_HPP -#include "matador/sql/statement_impl.hpp" +#include "matador/sql/interface/statement_impl.hpp" #include "postgres_parameter_binder.h" @@ -14,11 +14,11 @@ class postgres_statement final : public sql::statement_impl public: postgres_statement(PGconn *db, PGresult *result, std::string name, const sql::query_context &query); - size_t execute() override; - std::unique_ptr fetch() override; + utils::result execute() override; + utils::result, utils::error> fetch() override; void reset() override; protected: - sql::parameter_binder& binder() override; + utils::attribute_writer& binder() override; private: PGconn *db_{nullptr}; diff --git a/backends/postgres/src/postgres_connection.cpp b/backends/postgres/src/postgres_connection.cpp index aa093e5..a412411 100644 --- a/backends/postgres/src/postgres_connection.cpp +++ b/backends/postgres/src/postgres_connection.cpp @@ -3,67 +3,101 @@ #include "postgres_result_reader.hpp" #include "postgres_statement.hpp" +#include "matador/sql/error_code.hpp" #include "matador/sql/record.hpp" -#include +#include "matador/sql/internal/query_result_impl.hpp" + + #include namespace matador::backends::postgres { - postgres_connection::string_to_int_map postgres_connection::statement_name_map_{}; postgres_connection::postgres_connection(const sql::connection_info &info) -: connection_impl(info) {} + : connection_impl(info) { +} -void postgres_connection::open() -{ +utils::result postgres_connection::open() { if (is_open()) { - return; + return utils::ok(); } - std::string connection("user=" + info().user + " password=" + info().password + " host=" + info().hostname + " dbname=" + info().database + " port=" + std::to_string(info().port)); + const std::string connection( + "user=" + info().user + " password=" + info().password + " host=" + info().hostname + " dbname=" + info().database + + " port=" + std::to_string(info().port)); conn_ = PQconnectdb(connection.c_str()); if (PQstatus(conn_) == CONNECTION_BAD) { const std::string msg = PQerrorMessage(conn_); PQfinish(conn_); conn_ = nullptr; - throw_postgres_error(msg.c_str(), "postgres"); + return utils::failure(make_error(sql::error_code::OPEN_ERROR, nullptr, conn_, "Failed to connect")); } + + return utils::ok(); } -void postgres_connection::close() -{ +utils::result postgres_connection::close() { if (conn_) { PQfinish(conn_); conn_ = nullptr; } + + return utils::ok(); } -bool postgres_connection::is_open() -{ - return conn_ != nullptr; +utils::result postgres_connection::is_open() const { + return utils::ok(conn_ != nullptr); } -std::unique_ptr postgres_connection::fetch(const std::string &stmt) -{ - PGresult *res = PQexec(conn_, stmt.c_str()); +utils::result postgres_connection::is_valid() const { + return utils::ok(PQstatus(conn_) == CONNECTION_OK); +} - throw_postgres_error(res, conn_, "postgres", stmt); +utils::result postgres_connection::client_version() const { + const auto client_version = PQlibVersion(); + return utils::ok(utils::version{ + static_cast(client_version / 10000), + static_cast((client_version % 10000) / 100), + static_cast(client_version % 100) + }); +} - std::vector prototype; - auto num_col = PQnfields(res); - for (int i = 0; i < num_col; ++i) { - const char *col_name = PQfname(res, i); - auto type = PQftype(res, i); - auto size = PQfmod(res, i); - prototype.emplace_back(col_name); +utils::result postgres_connection::server_version() const { + const auto server_version = PQserverVersion(conn_); + + if (server_version == 0) { + return utils::failure(make_error(sql::error_code::FAILURE, nullptr, conn_, "Failed to get server version")); } - return std::move(std::make_unique(std::make_unique(res), std::move(prototype))); + + return utils::ok(utils::version{ + static_cast(server_version / 10000), + static_cast((server_version % 10000) / 100), + static_cast(server_version % 100) + }); } -std::string postgres_connection::generate_statement_name(const sql::query_context &query) -{ +utils::result, utils::error> postgres_connection::fetch(const sql::query_context &context) { + PGresult *res = PQexec(conn_, context.sql.c_str()); + + if (is_result_error(res)) { + return utils::failure(make_error(sql::error_code::FETCH_FAILED, res, conn_, "Failed to fetch", context.sql)); + } + +// std::vector prototype; +// const auto num_col = PQnfields(res); +// for (int i = 0; i < num_col; ++i) { +// const char *col_name = PQfname(res, i); +// auto type = PQftype(res, i); +// auto size = PQfmod(res, i); +// prototype.emplace_back(col_name); +// } + + return utils::ok(std::make_unique(std::make_unique(res), context.prototype)); +} + +std::string postgres_connection::generate_statement_name(const sql::query_context &query) { std::stringstream name; name << query.table.name << "_" << query.command_name; auto result = postgres_connection::statement_name_map_.find(name.str()); @@ -77,71 +111,85 @@ std::string postgres_connection::generate_statement_name(const sql::query_contex return name.str(); } -std::unique_ptr postgres_connection::prepare(sql::query_context context) -{ +utils::result, utils::error> postgres_connection::prepare(const sql::query_context &context) { auto statement_name = postgres_connection::generate_statement_name(context); - PGresult *result = PQprepare(conn_, statement_name.c_str(), context.sql.c_str(), static_cast(context.bind_vars.size()), nullptr); + PGresult *result = PQprepare(conn_, statement_name.c_str(), context.sql.c_str(), + static_cast(context.bind_vars.size()), nullptr); - throw_postgres_error(result, conn_, "postgres", context.sql); + if (is_result_error(result)) { + return utils::failure(make_error(sql::error_code::PREPARE_FAILED, result, conn_, "Failed to prepare", context.sql)); + } - return std::make_unique(conn_, result, statement_name, std::move(context)); + std::unique_ptr s(std::make_unique(conn_, result, statement_name, context)); + return utils::ok(std::move(s)); } -size_t postgres_connection::execute(const std::string &stmt) -{ +utils::result postgres_connection::execute(const std::string &stmt) { PGresult *res = PQexec(conn_, stmt.c_str()); - throw_postgres_error(res, conn_, "postgres", stmt); + if (const auto status = PQresultStatus(res); status != PGRES_COMMAND_OK && + status != PGRES_TUPLES_OK) { + return utils::failure(make_error(sql::error_code::FAILURE, res, conn_, "Failed to execute", stmt)); + } - const auto affected_rows = sql::to_long_long(PQcmdTuples(res)); + const auto affected_rows = utils::to(PQcmdTuples(res)); PQclear(res); - return affected_rows; + return utils::ok(static_cast(affected_rows)); } -sql::data_type_t string2type(const char *type) -{ +utils::basic_type string2type(const char *type) { if (strcmp(type, "int2") == 0) { - return sql::data_type_t::type_short; + return utils::basic_type::type_int16; } else if (strcmp(type, "int4") == 0) { - return sql::data_type_t::type_int; + return utils::basic_type::type_int32; } else if (strcmp(type, "int8") == 0) { - return sql::data_type_t::type_long_long; - } else if (strncmp(type, "int8", 6) == 0) { - return sql::data_type_t::type_long_long; + return utils::basic_type::type_int64; + } else if (strcmp(type, "bool") == 0) { + return utils::basic_type::type_bool; } else if (strcmp(type, "date") == 0) { - return sql::data_type_t::type_date; - } else if (strncmp(type, "timestamp", 8) == 0) { - return sql::data_type_t::type_time; + return utils::basic_type::type_date; + } else if (strcmp(type, "timestamp") == 0) { + return utils::basic_type::type_time; } else if (strcmp(type, "float4") == 0) { - return sql::data_type_t::type_float; + return utils::basic_type::type_float; } else if (strcmp(type, "float8") == 0) { - return sql::data_type_t::type_double; + return utils::basic_type::type_double; } else if (strncmp(type, "varchar", 7) == 0) { - return sql::data_type_t::type_varchar; - } else if (strncmp(type, "character varying", 7) == 0) { - return sql::data_type_t::type_varchar; - } else if (strncmp(type, "text", 0) == 0) { - return sql::data_type_t::type_text; + return utils::basic_type::type_varchar; + } else if (strcmp(type, "character varying") == 0) { + return utils::basic_type::type_varchar; + } else if (strcmp(type, "text") == 0) { + return utils::basic_type::type_text; + } else if (strcmp(type, "bytea") == 0) { + return utils::basic_type::type_blob; } else { - return sql::data_type_t::type_unknown; + return utils::basic_type::type_null; } } -std::vector postgres_connection::describe(const std::string &table) -{ - std::string stmt( - "SELECT ordinal_position, column_name, udt_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_schema='public' AND table_name='" + table + "'"); +utils::result, utils::error> postgres_connection::describe(const std::string &table) { + const std::string stmt( + "SELECT ordinal_position, column_name, udt_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_schema='public' AND table_name='" + + table + "'"); PGresult *res = PQexec(conn_, stmt.c_str()); - throw_postgres_error(res, conn_, "postgres", stmt); + if (is_result_error(res)) { + return utils::failure(make_error(sql::error_code::DESCRIBE_FAILED, res, conn_, "Failed to describe", stmt)); + } postgres_result_reader reader(res); std::vector prototype; - while (reader.fetch()) { + while (auto fetched = reader.fetch()) { + if (!fetched.is_ok()) { + return utils::failure(fetched.release_error()); + } + if (!*fetched) { + break; + } char *end = nullptr; // Todo: Handle error auto index = strtoul(reader.column(0), &end, 10) - 1; @@ -158,31 +206,39 @@ std::vector postgres_connection::describe(const std::str prototype.emplace_back(name, type, utils::null_attributes, null_opt, index); } - return std::move(prototype); + return utils::ok(prototype); } -bool postgres_connection::exists(const std::string &schema_name, const std::string &table_name) -{ - std::string stmt("SELECT 1 FROM information_schema.tables WHERE table_schema = '" + schema_name + "' AND table_name = '" + table_name + "'"); +utils::result postgres_connection::exists(const std::string &schema_name, const std::string &table_name) { + const std::string stmt( + "SELECT 1 FROM information_schema.tables WHERE table_schema = '" + schema_name + "' AND table_name = '" + table_name + + "'"); PGresult *res = PQexec(conn_, stmt.c_str()); - throw_postgres_error(res, conn_, "postgres", stmt); + if (is_result_error(res)) { + return utils::failure(make_error(sql::error_code::TABLE_EXISTS_FAILED, res, conn_, "Failed check if table exists", stmt)); + } - return sql::to_long_long(PQcmdTuples(res)) == 1; + return utils::ok(utils::to(PQcmdTuples(res)) == 1); +} + +std::string postgres_connection::to_escaped_string(const utils::blob& value) const +{ + size_t escapedDataLength; + unsigned char *escapedData = PQescapeByteaConn(conn_, value.data(), value.size(), &escapedDataLength); + + return {reinterpret_cast(escapedData), escapedDataLength-1}; } } -extern "C" -{ -MATADOR_POSTGRES_API matador::sql::connection_impl *create_database(const matador::sql::connection_info &info) -{ +extern "C" { +MATADOR_POSTGRES_API matador::sql::connection_impl *create_database(const matador::sql::connection_info &info) { return new matador::backends::postgres::postgres_connection(info); } -MATADOR_POSTGRES_API void destroy_database(matador::sql::connection_impl *db) -{ +MATADOR_POSTGRES_API void destroy_database(matador::sql::connection_impl *db) { delete db; } } diff --git a/backends/postgres/src/postgres_dialect.cpp b/backends/postgres/src/postgres_dialect.cpp index affc1d4..7cd2503 100644 --- a/backends/postgres/src/postgres_dialect.cpp +++ b/backends/postgres/src/postgres_dialect.cpp @@ -2,20 +2,28 @@ #include "matador/sql/dialect_builder.hpp" +#include "matador/utils/basic_types.hpp" + [[maybe_unused]] const matador::sql::dialect *get_dialect() { using namespace matador::sql; const static dialect d = dialect_builder::builder() .create() - .with_placeholder_func([](size_t index) { + .with_placeholder_func([](const size_t index) { return "$" + std::to_string(index); }) .with_token_replace_map({ - {dialect::token_t::BEGIN_BINARY_DATA, "E'\\"} + {dialect_token::BEGIN_BINARY_DATA, "'"} }) .with_data_type_replace_map({ - {data_type_t::type_blob, "BYTEA"} - }) + {matador::utils::basic_type::type_int8, "SMALLINT"}, + {matador::utils::basic_type::type_uint8, "SMALLINT"}, + {matador::utils::basic_type::type_float, "REAL"}, + {matador::utils::basic_type::type_double, "DOUBLE PRECISION"}, + {matador::utils::basic_type::type_time, "TIMESTAMP"}, + {matador::utils::basic_type::type_blob, "BYTEA"} + }) + .with_bool_strings("TRUE", "FALSE") .with_default_schema_name("public") .build(); return &d; diff --git a/backends/postgres/src/postgres_error.cpp b/backends/postgres/src/postgres_error.cpp index 11a93d8..cb8dd1b 100644 --- a/backends/postgres/src/postgres_error.cpp +++ b/backends/postgres/src/postgres_error.cpp @@ -6,6 +6,30 @@ namespace matador::backends::postgres { +utils::error make_error(const sql::error_code ec, const PGresult *res, const PGconn *db, const std::string &msg, + const std::string &sql) { + utils::error err(ec, msg); + err.add_error_info("dbms", "postgres"); + if (!sql.empty()) { + err.add_error_info("sql", sql); + } + if (res == nullptr) { + err.add_error_info("message", PQerrorMessage(db)); + } else if (const auto status = PQresultStatus(res); status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + err.add_error_info("message", PQresultErrorField(res, PG_DIAG_SQLSTATE)); + } + + return err; +} + +bool is_result_error(const PGresult *res) { + if (res == nullptr) { + return true; + } + const auto status = PQresultStatus(res); + return status != PGRES_TUPLES_OK && status == PGRES_COMMAND_OK; +} + void throw_postgres_error(const char *what, const std::string &source) { std::stringstream msg; @@ -26,8 +50,9 @@ void throw_postgres_error(PGresult *res, PGconn *db, const std::string &source, std::stringstream msg; msg << "postgres error (" << source << ", " << PQerrorMessage(db) << ": " << sql; throw std::logic_error(msg.str()); - } else if ((PQresultStatus(res) != PGRES_COMMAND_OK && - PQresultStatus(res) != PGRES_TUPLES_OK)) { + } + if (const auto status = PQresultStatus(res); status != PGRES_COMMAND_OK && + status != PGRES_TUPLES_OK) { std::stringstream msg; msg << "postgres error (" << source << ", " << PQresultErrorField(res, PG_DIAG_SQLSTATE) << ") " << PQerrorMessage(db) << ": " << sql; throw std::logic_error(msg.str()); diff --git a/backends/postgres/src/postgres_parameter_binder.cpp b/backends/postgres/src/postgres_parameter_binder.cpp index 610cc01..35f215d 100644 --- a/backends/postgres/src/postgres_parameter_binder.cpp +++ b/backends/postgres/src/postgres_parameter_binder.cpp @@ -1,29 +1,32 @@ #include "postgres_parameter_binder.h" +#include "matador/utils/string.hpp" + +#include + namespace matador::backends::postgres { - namespace detail { - -template < class T > -void bind_value(std::vector &strings, std::vector ¶ms, size_t index, T &x) -{ - strings[index] = std::to_string(x); - params[index] = strings[index].c_str(); +template +void bind_value(postgres_parameter_binder::bind_data &data, const size_t index, const T &x) { + data.strings[index] = std::to_string(x); + data.values[index] = data.strings[index].c_str(); + data.lengths[index] = static_cast(data.strings[index].size()); + data.formats[index] = 0; } -template <> -void bind_value(std::vector &strings, std::vector ¶ms, size_t index, char &x) -{ - strings[index] = std::to_string(x); - params[index] = strings[index].data(); -} +// template<> +// void bind_value(postgres_parameter_binder::bind_data &data, size_t index, const char &x) { +// data.strings[index] = std::to_string(x); +// data.values[index] = data.strings[index].data(); +// data.formats[index] = 0; +// } -template <> -void bind_value(std::vector &strings, std::vector ¶ms, size_t index, unsigned char &x) -{ - strings[index] = std::to_string(x); - params[index] = strings[index].data(); -} +// template<> +// void bind_value(postgres_parameter_binder::bind_data &data, size_t index, const unsigned char &x) { +// data.strings[index] = std::to_string(x); +// data.values[index] = data.strings[index].data(); +// data.formats[index] = 0; +// } //template <> //void bind_value(std::vector &strings, std::vector ¶ms, size_t &index, const matador::date &x) @@ -40,108 +43,111 @@ void bind_value(std::vector &strings, std::vector &par // params[index] = strings[index].c_str(); // ++index; //} - } -postgres_parameter_binder::postgres_parameter_binder(size_t size) -: strings_(size) -, params_(size) +postgres_parameter_binder::bind_data::bind_data(const size_t size) +: strings(size) +, bytes(size) +, values(size) +, lengths(size) +, formats(size) {} -void postgres_parameter_binder::bind(size_t pos, char i) -{ - detail::bind_value(strings_, params_, pos, i); +postgres_parameter_binder::postgres_parameter_binder(size_t size) +: bind_data_(size) +{} + +void postgres_parameter_binder::write_value(const size_t pos, const int8_t &x) { + detail::bind_value(bind_data_, pos, x); } -void postgres_parameter_binder::bind(size_t pos, short i) -{ - detail::bind_value(strings_, params_, pos, i); +void postgres_parameter_binder::write_value(const size_t pos, const int16_t &x) { + detail::bind_value(bind_data_, pos, x); } -void postgres_parameter_binder::bind(size_t pos, int i) -{ - detail::bind_value(strings_, params_, pos, i); +void postgres_parameter_binder::write_value(const size_t pos, const int32_t &x) { + detail::bind_value(bind_data_, pos, x); } -void postgres_parameter_binder::bind(size_t pos, long i) -{ - detail::bind_value(strings_, params_, pos, i); +void postgres_parameter_binder::write_value(const size_t pos, const int64_t &x) { + detail::bind_value(bind_data_, pos, x); } -void postgres_parameter_binder::bind(size_t pos, long long int i) -{ - detail::bind_value(strings_, params_, pos, i); +void postgres_parameter_binder::write_value(const size_t pos, const uint8_t &x) { + detail::bind_value(bind_data_, pos, x); } -void postgres_parameter_binder::bind(size_t pos, unsigned char i) -{ - detail::bind_value(strings_, params_, pos, i); +void postgres_parameter_binder::write_value(const size_t pos, const uint16_t &x) { + detail::bind_value(bind_data_, pos, x); } -void postgres_parameter_binder::bind(size_t pos, unsigned short i) -{ - detail::bind_value(strings_, params_, pos, i); +void postgres_parameter_binder::write_value(const size_t pos, const uint32_t &x) { + detail::bind_value(bind_data_, pos, x); } -void postgres_parameter_binder::bind(size_t pos, unsigned int i) -{ - detail::bind_value(strings_, params_, pos, i); +void postgres_parameter_binder::write_value(const size_t pos, const uint64_t &x) { + detail::bind_value(bind_data_, pos, x); } -void postgres_parameter_binder::bind(size_t pos, unsigned long i) -{ - detail::bind_value(strings_, params_, pos, i); +void postgres_parameter_binder::write_value(const size_t pos, const bool &x) { + detail::bind_value(bind_data_, pos, x); } -void postgres_parameter_binder::bind(size_t pos, unsigned long long int i) -{ - detail::bind_value(strings_, params_, pos, i); +void postgres_parameter_binder::write_value(const size_t pos, const float &x) { + detail::bind_value(bind_data_, pos, x); } -void postgres_parameter_binder::bind(size_t pos, bool b) -{ - detail::bind_value(strings_, params_, pos, b); +void postgres_parameter_binder::write_value(const size_t pos, const double &x) { + detail::bind_value(bind_data_, pos, x); } -void postgres_parameter_binder::bind(size_t pos, float d) -{ - detail::bind_value(strings_, params_, pos, d); +void postgres_parameter_binder::write_value(const size_t pos, const char *x) { + bind_data_.values[pos] = x; + bind_data_.lengths[pos] = static_cast(std::strlen(x)); + bind_data_.formats[pos] = 0; } -void postgres_parameter_binder::bind(size_t pos, double d) -{ - detail::bind_value(strings_, params_, pos, d); +void postgres_parameter_binder::write_value(const size_t pos, const char *x, size_t /*size*/) { + bind_data_.values[pos] = x; + bind_data_.lengths[pos] = static_cast(std::strlen(x)); + bind_data_.formats[pos] = 0; } -void postgres_parameter_binder::bind(size_t pos, const char *str) -{ - params_[pos] = str; +void postgres_parameter_binder::write_value(const size_t pos, const std::string &x) { + bind_data_.values[pos] = x.data(); + bind_data_.lengths[pos] = static_cast(x.size()); + bind_data_.formats[pos] = 0; } -void postgres_parameter_binder::bind(size_t pos, const char *str, size_t size) -{ - params_[pos] = str; +void postgres_parameter_binder::write_value(const size_t pos, const std::string &x, size_t /*size*/) { + write_value(pos, x); } -void postgres_parameter_binder::bind(size_t pos, const std::string &str) -{ - strings_[pos] = str; - params_[pos] = strings_[pos].c_str(); +void postgres_parameter_binder::write_value(const size_t pos, const time &/*x*/) { + // bind_data_.strings[pos] = utils::to_string(x, "%Y-%m-%d %T.%f"); + bind_data_.values[pos] = bind_data_.strings[pos].data(); + bind_data_.lengths[pos] = static_cast(bind_data_.strings[pos].size()); + bind_data_.formats[pos] = 0; } -void postgres_parameter_binder::bind(size_t pos, const std::string &str, size_t size) -{ - bind(pos, str); +void postgres_parameter_binder::write_value(const size_t pos, const date &/*x*/) { + // bind_data_.strings[pos] = utils::to_string(x, utils::date_format::ISO8601); + bind_data_.values[pos] = bind_data_.strings[pos].data(); + bind_data_.lengths[pos] = static_cast(bind_data_.strings[pos].size()); + bind_data_.formats[pos] = 0; } -void postgres_parameter_binder::bind(size_t pos, const utils::blob &blob) -{ - params_[pos] = ""; +void postgres_parameter_binder::write_value(const size_t pos, const utils::blob &x) { + bind_data_.bytes[pos] = x; + bind_data_.values[pos] = reinterpret_cast(bind_data_.bytes[pos].data()); + bind_data_.lengths[pos] = static_cast(bind_data_.bytes[pos].size()); + bind_data_.formats[pos] = 1; } -const std::vector &postgres_parameter_binder::params() const -{ - return params_; +void postgres_parameter_binder::write_value(const size_t /*pos*/, const utils::value &/*x*/, size_t /*size*/) {} + +const postgres_parameter_binder::bind_data &postgres_parameter_binder::params() const { + return bind_data_; } -} \ No newline at end of file +} diff --git a/backends/postgres/src/postgres_result_reader.cpp b/backends/postgres/src/postgres_result_reader.cpp index 767f60c..5c0bb46 100644 --- a/backends/postgres/src/postgres_result_reader.cpp +++ b/backends/postgres/src/postgres_result_reader.cpp @@ -1,6 +1,7 @@ #include "postgres_result_reader.hpp" -#include "matador/sql/to_value.hpp" +#include "matador/utils/convert.hpp" +#include "matador/utils/value.hpp" namespace matador::backends::postgres { @@ -22,14 +23,209 @@ size_t postgres_result_reader::column_count() const return column_count_; } -const char *postgres_result_reader::column(size_t index) const +const char *postgres_result_reader::column( const size_t index) const { return PQgetvalue(result_, static_cast(row_index_), static_cast(index)); } -bool postgres_result_reader::fetch() -{ - return ++row_index_ < row_count_; +utils::result postgres_result_reader::fetch() { return utils::ok(++row_index_ < row_count_); } + +size_t postgres_result_reader::start_column_index() const { + return 0; } -} \ No newline at end of file +void postgres_result_reader::read_value(const char * /*id*/, const size_t index, int8_t &value) { + if (auto res = utils::to(column(index)); res.is_ok()) { + value = res.value(); + } +} + +void postgres_result_reader::read_value(const char * /*id*/, const size_t index, int16_t &value) { + if (auto res = utils::to(column(index)); res.is_ok()) { + value = res.value(); + } +} + +void postgres_result_reader::read_value(const char * /*id*/, const size_t index, int32_t &value) { + if (auto res = utils::to(column(index)); res.is_ok()) { + value = res.value(); + } +} + +void postgres_result_reader::read_value(const char * /*id*/, const size_t index, int64_t &value) { + if (auto res = utils::to(column(index)); res.is_ok()) { + value = res.value(); + } +} + +void postgres_result_reader::read_value(const char * /*id*/, const size_t index, uint8_t &value) { + if (auto res = utils::to(column(index)); res.is_ok()) { + value = res.value(); + } +} + +void postgres_result_reader::read_value(const char * /*id*/, const size_t index, uint16_t &value) { + if (auto res = utils::to(column(index)); res.is_ok()) { + value = res.value(); + } +} + +void postgres_result_reader::read_value(const char * /*id*/, const size_t index, uint32_t &value) { + if (auto res = utils::to(column(index)); res.is_ok()) { + value = res.value(); + } +} + +void postgres_result_reader::read_value(const char * /*id*/, const size_t index, uint64_t &value) { + if (auto res = utils::to(column(index)); res.is_ok()) { + value = res.value(); + } +} + +void postgres_result_reader::read_value(const char * /*id*/, const size_t index, bool &value) { + if (auto res = utils::to(column(index)); res.is_ok()) { + value = res.value(); + } +} + +void postgres_result_reader::read_value(const char * /*id*/, const size_t index, float &value) { + if (auto res = utils::to(column(index)); res.is_ok()) { + value = res.value(); + } +} + +void postgres_result_reader::read_value(const char * /*id*/, const size_t index, double &value) { + if (auto res = utils::to(column(index)); res.is_ok()) { + value = res.value(); + } +} + +void postgres_result_reader::read_value(const char * /*id*/, const size_t index, time &value) { + // if (const auto val = column(index); strlen(val) > 0) { + // value = time::parse(val, "%Y-%m-%d %T.%f"); + // } +} + +void postgres_result_reader::read_value(const char * /*id*/, const size_t index, date &value) { + // if (const auto val = column(index); strlen(val) > 0) { + // value.set(val, matador::utils::date_format::ISO8601); + // } +} + +void postgres_result_reader::read_value(const char * /*id*/, const size_t index, char *value, size_t size) { + auto val = column(index); + if (const size_t len = strlen(val); len > size) { +#ifdef _MSC_VER + strncpy_s(value, size, val, len); +#else + strncpy(value, val, size); +#endif + value[size-1] = '\n'; + } else { +#ifdef _MSC_VER + strcpy_s(value, size, val); +#else + strcpy(value, val); +#endif + } +} + +void postgres_result_reader::read_value(const char * /*id*/, const size_t index, std::string &value) { + value.assign(column(index)); +} + +void postgres_result_reader::read_value(const char * /*id*/, const size_t index, std::string &value, size_t /*s*/) { + value.assign(column(index)); +} + +void postgres_result_reader::read_value( const char* id, const size_t index, utils::blob& value ) +{ + const auto *data = reinterpret_cast(column(index)); + // auto length = PQgetlength(result_, row_index_, static_cast(index)); + + size_t length; + unsigned char* unescaped = PQunescapeBytea(data, &length); + + value.assign(unescaped, unescaped+length); +} + +template +void set_value(const char* str, utils::value& value) { + if (const auto res = utils::to(str); res.is_ok()) { + value = res.value(); + } +} + +template <> +void set_value(const char* str, utils::value& value) { + size_t length; + unsigned char* unescaped = PQunescapeBytea(reinterpret_cast(str), &length); + + value = utils::blob(unescaped, unescaped+length); +} + +void postgres_result_reader::read_value(const char * /*id*/, const size_t index, utils::value &val, size_t) { + switch (val.type()) { + case utils::basic_type::type_int8: + set_value(column(index), val); + break; + case utils::basic_type::type_int16: + set_value(column(index), val); + break; + case utils::basic_type::type_int32: + set_value(column(index), val); + break; + case utils::basic_type::type_int64: + set_value(column(index), val); + break; + case utils::basic_type::type_uint8: + set_value(column(index), val); + break; + case utils::basic_type::type_uint16: + set_value(column(index), val); + break; + case utils::basic_type::type_uint32: + set_value(column(index), val); + break; + case utils::basic_type::type_uint64: + set_value(column(index), val); + break; + case utils::basic_type::type_float: + set_value(column(index), val); + break; + case utils::basic_type::type_double: + set_value(column(index), val); + break; + case utils::basic_type::type_bool: + set_value(column(index), val); + break; + case utils::basic_type::type_text: + case utils::basic_type::type_varchar: { + if (const auto *column_value = column(index); column_value == nullptr) { + val = std::string{}; + } else { + val = std::string{column_value}; + } + break; + } + case utils::basic_type::type_time: + case utils::basic_type::type_date: { + val = std::string{column(index)}; + break; + } + case utils::basic_type::type_null: { + val = nullptr_t{}; + break; + } + case utils::basic_type::type_blob: { + set_value(column(index), val); + break; + } + } +} + +utils::attribute_reader &postgres_result_reader::result_binder() { + return empty_binder_; +} + +} // namespace matador::backends::postgres diff --git a/backends/postgres/src/postgres_statement.cpp b/backends/postgres/src/postgres_statement.cpp index d329f47..c65a7a6 100644 --- a/backends/postgres/src/postgres_statement.cpp +++ b/backends/postgres/src/postgres_statement.cpp @@ -12,27 +12,48 @@ postgres_statement::postgres_statement(PGconn *db, PGresult *result, std::string , binder_(query_.bind_vars.size()) {} -size_t postgres_statement::execute() +utils::result postgres_statement::execute() { - PGresult *res = PQexecPrepared(db_, name_.c_str(), static_cast(binder_.params().size()), binder_.params().data(), nullptr, nullptr, 0); + PGresult *res = PQexecPrepared(db_, + name_.c_str(), + static_cast(binder_.params().values.size()), + binder_.params().values.data(), + binder_.params().lengths.data(), + binder_.params().formats.data(), + 0); - throw_postgres_error(res, db_, "postgres", query_.sql); + if (is_result_error(res)) { + return utils::failure(make_error(sql::error_code::EXECUTE_FAILED, res, db_, "Failed to execute statement", query_.sql)); + } - return std::stoul(PQcmdTuples(res)); + const auto *tuples = PQcmdTuples(res); + if (strlen(tuples) == 0) { + return utils::ok(static_cast(0)); + } + + return utils::ok(static_cast(std::stoul(tuples))); } -std::unique_ptr postgres_statement::fetch() +utils::result, utils::error> postgres_statement::fetch() { - PGresult *res = PQexecPrepared(db_, name_.c_str(), static_cast(binder_.params().size()), binder_.params().data(), nullptr, nullptr, 0); + PGresult *res = PQexecPrepared(db_, + name_.c_str(), + static_cast(binder_.params().values.size()), + binder_.params().values.data(), + binder_.params().lengths.data(), + binder_.params().formats.data(), + 0); - throw_postgres_error(res, db_, "postgres", query_.sql); + if (is_result_error(res)) { + return utils::failure(make_error(sql::error_code::FETCH_FAILED, res, db_, "Failed to fetch statement", query_.sql)); + } - return std::move(std::make_unique(std::make_unique(res), std::move(query_.prototype))); + return utils::ok(std::make_unique(std::make_unique(res), query_.prototype)); } void postgres_statement::reset() {} -sql::parameter_binder& postgres_statement::binder() +utils::attribute_writer& postgres_statement::binder() { return binder_; } diff --git a/backends/postgres/test/CMakeLists.txt b/backends/postgres/test/CMakeLists.txt index 7131435..88cb8c7 100644 --- a/backends/postgres/test/CMakeLists.txt +++ b/backends/postgres/test/CMakeLists.txt @@ -1,45 +1,50 @@ -Include(FetchContent) - -FetchContent_Declare( - Catch2 - GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v3.5.4 # or a later release -) - -FetchContent_MakeAvailable(Catch2) +CPMAddPackage("gh:catchorg/Catch2@3.7.1") list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) -include(CTest) -include(Catch) -set(POSTGRES_CONNECTION_STRING "postgres://test:test123@127.0.0.1:15432/test") +set(POSTGRES_CONNECTION_STRING "postgres://test:test123@localhost:15432/testdb") configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/postgres/test/connection.hpp @ONLY IMMEDIATE) message(STATUS "postgresql connection string: ${POSTGRES_CONNECTION_STRING}") set(TEST_SOURCES - ../../tests/QueryTest.cpp - ../../tests/ConnectionTest.cpp - ../../tests/QueryRecordTest.cpp - ../../tests/StatementTest.cpp - ../../tests/TypeTraitsTest.cpp - ../../tests/StatementCacheTest.cpp - ../../tests/SessionTest.cpp) + ../../../test/models/coordinate.hpp + ../../../test/models/location.hpp + ../../../test/models/types.hpp + ../../../test/backends/ColorEnumTraits.cpp + ../../../test/backends/ColorEnumTraits.hpp + ../../../test/backends/ConnectionTest.cpp + ../../../test/backends/QueryBasicTest.cpp + ../../../test/backends/QueryFixture.cpp + ../../../test/backends/QueryFixture.hpp + ../../../test/backends/QueryRecordTest.cpp + ../../../test/backends/QueryStatementTests.cpp + ../../../test/backends/QueryTest.cpp + ../../../test/backends/SessionFixture.cpp + ../../../test/backends/SessionFixture.hpp + ../../../test/backends/SessionTest.cpp + ../../../test/backends/StatementCacheTest.cpp + ../../../test/backends/StatementTest.cpp + ../../../test/backends/TypeTraitsTest.cpp +) -set(LIBRARY_TEST_TARGET postgres_tests) +set(LIBRARY_TEST_TARGET PostgresTests) add_executable(${LIBRARY_TEST_TARGET} ${TEST_SOURCES}) target_link_libraries(${LIBRARY_TEST_TARGET} PRIVATE Catch2::Catch2WithMain - matador + matador-utils + matador-query ${CMAKE_DL_LIBS} ${PostgreSQL_LIBRARY}) +add_dependencies(${LIBRARY_TEST_TARGET} matador-postgres) + target_include_directories(${LIBRARY_TEST_TARGET} PUBLIC $/include PRIVATE $/test PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) -catch_discover_tests(${LIBRARY_TEST_TARGET} TEST_SUFFIX " (PostgreSQL)") +#catch_discover_tests(${LIBRARY_TEST_TARGET} TEST_SUFFIX " (PostgreSQL)") diff --git a/backends/sqlite/CMakeLists.txt b/backends/sqlite/CMakeLists.txt deleted file mode 100644 index d1faf1e..0000000 --- a/backends/sqlite/CMakeLists.txt +++ /dev/null @@ -1,37 +0,0 @@ -set(HEADER - include/sqlite_connection.hpp - include/sqlite_error.hpp - include/sqlite_dialect.hpp - include/sqlite_result_reader.hpp - include/sqlite_statement.hpp - include/sqlite_parameter_binder.h - include/sqlite_prepared_result_reader.hpp -) - -set(SOURCES - src/sqlite_connection.cpp - src/sqlite_error.cpp - src/sqlite_dialect.cpp - src/sqlite_result_reader.cpp - src/sqlite_statement.cpp - src/sqlite_parameter_binder.cpp - src/sqlite_prepared_result_reader.cpp -) - -set(LIBRARY_TARGET matador-sqlite) - -add_subdirectory(test) - -add_library(${LIBRARY_TARGET} MODULE ${SOURCES} ${HEADER}) - -set_target_properties(${LIBRARY_TARGET} - PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/backends" -) - -target_include_directories(${LIBRARY_TARGET} PRIVATE - ${PROJECT_SOURCE_DIR}/include - ${PROJECT_SOURCE_DIR}/backends/sqlite/include - ${SQLite3_INCLUDE_DIRS}) - -target_link_libraries(${LIBRARY_TARGET} matador ${SQLite3_LIBRARIES}) diff --git a/backends/sqlite/include/sqlite_connection.hpp b/backends/sqlite/include/sqlite_connection.hpp deleted file mode 100644 index f5e3f12..0000000 --- a/backends/sqlite/include/sqlite_connection.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef QUERY_SQLITE_CONNECTION_HPP -#define QUERY_SQLITE_CONNECTION_HPP - -#ifdef _MSC_VER -#ifdef matador_sqlite_EXPORTS -#define MATADOR_SQLITE_API __declspec(dllexport) -#else -#define MATADOR_SQLITE_API __declspec(dllimport) -#endif -#pragma warning(disable: 4355) -#else -#define MATADOR_SQLITE_API -#endif - -#include "matador/sql/connection_impl.hpp" - -#include "sqlite_result_reader.hpp" - -#include - -namespace matador::backends::sqlite { - -class sqlite_connection : public matador::sql::connection_impl -{ -public: - explicit sqlite_connection(const sql::connection_info &info); - void open() override; - void close() override; - bool is_open() override; - - std::unique_ptr fetch(const std::string &stmt) override; - std::unique_ptr prepare(sql::query_context query) override; - - size_t execute(const std::string &stmt) override; - - std::vector describe(const std::string& table) override; - - bool exists(const std::string &schema_name, const std::string &table_name) override; - -private: - struct fetch_context - { - std::vector prototype; - sqlite_result_reader::rows rows; - }; - -private: - static int parse_result(void* param, int column_count, char** values, char** columns); - - fetch_context fetch_internal(const std::string &stmt); - -private: - sqlite3 *db_{}; -}; - -} - -extern "C" -{ -MATADOR_SQLITE_API matador::sql::connection_impl* create_database(const matador::sql::connection_info &info); - -MATADOR_SQLITE_API void destroy_database(matador::sql::connection_impl *db); -} - -#endif //QUERY_SQLITE_CONNECTION_HPP diff --git a/backends/sqlite/include/sqlite_dialect.hpp b/backends/sqlite/include/sqlite_dialect.hpp deleted file mode 100644 index 033b619..0000000 --- a/backends/sqlite/include/sqlite_dialect.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef QUERY_SQLITE_DIALECT_HPP -#define QUERY_SQLITE_DIALECT_HPP - -#ifdef _MSC_VER -#ifdef matador_sqlite_EXPORTS -#define MATADOR_SQLITE_API __declspec(dllexport) -#else -#define MATADOR_SQLITE_API __declspec(dllimport) -#endif -#pragma warning(disable: 4355) -#else -#define MATADOR_SQLITE_API -#endif - -#include "matador/sql/dialect.hpp" - -extern "C" [[maybe_unused]] MATADOR_SQLITE_API const matador::sql::dialect* get_dialect(); - -#endif //QUERY_SQLITE_DIALECT_HPP diff --git a/backends/sqlite/include/sqlite_error.hpp b/backends/sqlite/include/sqlite_error.hpp deleted file mode 100644 index 722bf6a..0000000 --- a/backends/sqlite/include/sqlite_error.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef QUERY_SQLITE_ERROR_HPP -#define QUERY_SQLITE_ERROR_HPP - -#include - -struct sqlite3; - -namespace matador::backends::sqlite { - -void throw_sqlite_error(int ec, sqlite3 *db, const std::string &source); -void throw_sqlite_error(int ec, sqlite3 *db, const std::string &source, const std::string &sql); - -} - -#endif //QUERY_SQLITE_ERROR_HPP diff --git a/backends/sqlite/include/sqlite_parameter_binder.h b/backends/sqlite/include/sqlite_parameter_binder.h deleted file mode 100644 index f4efd59..0000000 --- a/backends/sqlite/include/sqlite_parameter_binder.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef QUERY_SQLITE_PARAMETER_BINDER_H -#define QUERY_SQLITE_PARAMETER_BINDER_H - -#include "matador/sql/parameter_binder.hpp" - -#include - -#include -#include - -namespace matador::backends::sqlite { - -class sqlite_parameter_binder final : public sql::parameter_binder -{ -public: - explicit sqlite_parameter_binder(sqlite3 *db, sqlite3_stmt *stmt); - - void bind(size_t pos, char i) override; - void bind(size_t pos, short i) override; - void bind(size_t pos, int i) override; - void bind(size_t pos, long i) override; - void bind(size_t pos, long long int i) override; - void bind(size_t pos, unsigned char i) override; - void bind(size_t pos, unsigned short i) override; - void bind(size_t pos, unsigned int i) override; - void bind(size_t pos, unsigned long i) override; - void bind(size_t pos, unsigned long long int i) override; - void bind(size_t pos, bool b) override; - void bind(size_t pos, float d) override; - void bind(size_t pos, double d) override; - void bind(size_t pos, const char *string) override; - void bind(size_t pos, const char *str, size_t size) override; - void bind(size_t pos, const std::string &str) override; - void bind(size_t pos, const std::string &str, size_t size) override; - void bind(size_t pos, const utils::blob &blob) override; -private: - sqlite3 *db_{nullptr}; - sqlite3_stmt *stmt_{nullptr}; - - std::vector > host_strings_; -}; -} - -#endif //QUERY_SQLITE_PARAMETER_BINDER_H diff --git a/backends/sqlite/include/sqlite_prepared_result_reader.hpp b/backends/sqlite/include/sqlite_prepared_result_reader.hpp deleted file mode 100644 index c90eff6..0000000 --- a/backends/sqlite/include/sqlite_prepared_result_reader.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef QUERY_SQLITE_PREPARED_RESULT_READER_HPP -#define QUERY_SQLITE_PREPARED_RESULT_READER_HPP - -#include "matador/sql/query_result_reader.hpp" - -#include - -namespace matador::backends::sqlite { - -class sqlite_prepared_result_reader final : public sql::query_result_reader -{ -public: - sqlite_prepared_result_reader(sqlite3 *db, sqlite3_stmt *stmt); - - [[nodiscard]] size_t column_count() const override; - [[nodiscard]] const char *column(size_t index) const override; - bool fetch() override; - - void read_value(const char *id, size_t index, char &value) override; - void read_value(const char *id, size_t index, short &value) override; - void read_value(const char *id, size_t index, int &value) override; - void read_value(const char *id, size_t index, long &value) override; - void read_value(const char *id, size_t index, long long int &value) override; - void read_value(const char *id, size_t index, unsigned char &value) override; - void read_value(const char *id, size_t index, unsigned short &value) override; - void read_value(const char *id, size_t index, unsigned int &value) override; - void read_value(const char *id, size_t index, unsigned long &value) override; - void read_value(const char *id, size_t index, unsigned long long int &value) override; - void read_value(const char *id, size_t index, bool &value) override; - void read_value(const char *id, size_t index, float &value) override; - void read_value(const char *id, size_t index, double &value) override; - void read_value(const char *id, size_t index, char *value, size_t s) override; - void read_value(const char *id, size_t index, std::string &value) override; - void read_value(const char *id, size_t index, std::string &value, size_t s) override; - void read_value(const char *id, size_t index, sql::value &val, size_t size) override; - -private: - sqlite3 *db_{nullptr}; - sqlite3_stmt *stmt_{nullptr}; -}; -} -#endif //QUERY_SQLITE_PREPARED_RESULT_READER_HPP diff --git a/backends/sqlite/include/sqlite_result_reader.hpp b/backends/sqlite/include/sqlite_result_reader.hpp deleted file mode 100644 index 0e0d712..0000000 --- a/backends/sqlite/include/sqlite_result_reader.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef QUERY_SQLITE_RESULT_READER_HPP -#define QUERY_SQLITE_RESULT_READER_HPP - -#include "matador/sql/query_result_reader.hpp" - -#include - -namespace matador::backends::sqlite { - -class sqlite_result_reader final : public sql::query_result_reader -{ -public: - using columns = std::vector; - using rows = std::vector; - -public: - sqlite_result_reader(rows result, size_t column_count); - ~sqlite_result_reader() override; - - [[nodiscard]] size_t column_count() const override; - - [[nodiscard]] const char* column(size_t index) const override; - [[nodiscard]] bool fetch() override; - -private: - rows result_; - long long row_index_ = -1; - size_t column_count_{}; -}; - -} - -#endif //QUERY_SQLITE_RESULT_READER_HPP diff --git a/backends/sqlite/include/sqlite_statement.hpp b/backends/sqlite/include/sqlite_statement.hpp deleted file mode 100644 index fc60b87..0000000 --- a/backends/sqlite/include/sqlite_statement.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef QUERY_SQLITE_STATEMENT_HPP -#define QUERY_SQLITE_STATEMENT_HPP - -#include "matador/sql/statement_impl.hpp" - -#include "sqlite_parameter_binder.h" - -namespace matador::backends::sqlite { - -class sqlite_statement final : public sql::statement_impl -{ -public: - sqlite_statement(sqlite3 *db, sqlite3_stmt *stmt, const sql::query_context &query); - ~sqlite_statement(); - - size_t execute() override; - std::unique_ptr fetch() override; - void reset() override; -protected: - sql::parameter_binder& binder() override; - -private: - sqlite3 *db_{nullptr}; - sqlite3_stmt *stmt_{nullptr}; - - sqlite_parameter_binder binder_; -}; - -} - -#endif //QUERY_SQLITE_STATEMENT_HPP diff --git a/backends/sqlite/src/sqlite_connection.cpp b/backends/sqlite/src/sqlite_connection.cpp deleted file mode 100644 index 1034cc1..0000000 --- a/backends/sqlite/src/sqlite_connection.cpp +++ /dev/null @@ -1,202 +0,0 @@ -#include "sqlite_connection.hpp" -#include "sqlite_error.hpp" -#include "sqlite_result_reader.hpp" -#include "sqlite_statement.hpp" - -#include "matador/sql/record.hpp" - -#include -#include -#include - -namespace matador::backends::sqlite { - -sqlite_connection::sqlite_connection(const sql::connection_info &info) -: connection_impl(info) { -} - -void sqlite_connection::open() -{ - if (is_open()) { - return; - } - - const auto ret = sqlite3_open(info().database.c_str(), &db_); - - if (ret != SQLITE_OK) { - throw_sqlite_error(ret, db_, "open"); - } -} - -void sqlite_connection::close() -{ - int ret = sqlite3_close(db_); - - throw_sqlite_error(ret, db_, "close"); - - db_ = nullptr; -} - -bool sqlite_connection::is_open() -{ - return db_ != nullptr; -} - -int sqlite_connection::parse_result(void* param, int column_count, char** values, char** columns) -{ - auto *context = static_cast(param); - - sqlite_result_reader::columns column; - for(int i = 0; i < column_count; ++i) { - // copy and store column data; - if (values[i] == nullptr) { - auto val = new char[1]; - val[0] = '\0'; - column.push_back(val); - } else { - size_t size = strlen(values[i]); - auto val = new char[size + 1]; - std::memcpy(val, values[i], size); - val[size] = '\0'; - column.push_back(val); - } - } - context->rows.emplace_back(column); - - if (context->prototype.empty()) { - for(int i = 0; i < column_count; ++i) { - context->prototype.emplace_back(columns[i]); - } - } - - return 0; -} - -sqlite_connection::fetch_context sqlite_connection::fetch_internal(const std::string &stmt) -{ - fetch_context context; - char *errmsg = nullptr; - const int ret = sqlite3_exec(db_, stmt.c_str(), parse_result, &context, &errmsg); - - throw_sqlite_error(ret, db_, "sqlite", stmt); - - return context; -} - -size_t sqlite_connection::execute(const std::string &stmt) -{ - char *errmsg = nullptr; - int ret = sqlite3_exec(db_, stmt.c_str(), nullptr, nullptr, &errmsg); - - throw_sqlite_error(ret, db_, "sqlite", stmt); - - return sqlite3_changes(db_); -} - -std::unique_ptr sqlite_connection::fetch(const std::string &stmt) -{ - auto context = fetch_internal(stmt); - - return std::move(std::make_unique(std::make_unique(std::move(context.rows), context.prototype.size()), std::move(context.prototype))); -} - -std::unique_ptr sqlite_connection::prepare(sql::query_context query) -{ - sqlite3_stmt *stmt{}; - int ret = sqlite3_prepare_v2(db_, query.sql.c_str(), static_cast(query.sql.size()), &stmt, nullptr); - throw_sqlite_error(ret, db_, "sqlite3_prepare_v2", query.sql); - - return std::make_unique(db_, stmt, query); -} - -sql::data_type_t string2type(const char *type) -{ - if (strncmp(type, "INTEGER", 7) == 0) { - return sql::data_type_t::type_int; - } else if (strncmp(type, "TINYINT", 7) == 0) { - return sql::data_type_t::type_char; - } else if (strncmp(type, "SMALLINT", 8) == 0) { - return sql::data_type_t::type_short; - } else if (strncmp(type, "BIGINT", 6) == 0) { - return sql::data_type_t::type_long_long; - } else if (strcmp(type, "BOOLEAN") == 0) { - return sql::data_type_t::type_bool; - } else if (strcmp(type, "REAL") == 0) { - return sql::data_type_t::type_double; - } else if (strcmp(type, "FLOAT") == 0) { - return sql::data_type_t::type_float; - } else if (strcmp(type, "DOUBLE") == 0) { - return sql::data_type_t::type_double; - } else if (strcmp(type, "BLOB") == 0) { - return sql::data_type_t::type_blob; - } else if (strcmp(type, "NULL") == 0) { - return sql::data_type_t::type_null; - } else if (strncmp(type, "VARCHAR", 7) == 0) { - return sql::data_type_t::type_varchar; - } else if (strcmp(type, "DATE") == 0) { - return sql::data_type_t::type_date; - } else if (strcmp(type, "DATETIME") == 0) { - return sql::data_type_t::type_time; - } else if (strcmp(type, "TEXT") == 0) { - return sql::data_type_t::type_text; - } else { - return sql::data_type_t::type_unknown; - } -} - -std::vector sqlite_connection::describe(const std::string& table) -{ - const auto result = fetch_internal("PRAGMA table_info(" + table + ")"); - - sqlite_result_reader reader(result.rows, result.prototype.size()); - std::vector prototype; - while (reader.fetch()) { - char *end = nullptr; - // Todo: add index to column - auto index = strtoul(reader.column(0), &end, 10); - std::string name = reader.column(1); - - auto type = (string2type(reader.column(2))); - end = nullptr; - sql::null_option null_opt{sql::null_option::NULLABLE}; - if (strtoul(reader.column(3), &end, 10) == 0) { - null_opt = sql::null_option::NOT_NULL; - } - // f.default_value(res->column(4)); - prototype.emplace_back(name, type, utils::null_attributes, null_opt, index); - } - - return std::move(prototype); -} - -bool sqlite_connection::exists(const std::string &schema_name, const std::string &table_name) -{ - const auto result = fetch_internal("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND tbl_name='" + table_name + "' LIMIT 1"); - sqlite_result_reader reader(result.rows, result.prototype.size()); - - if (!reader.fetch()) { - // Todo: throw an exception? - return false; - } - - int v{}; - reader.read_value(nullptr, 0, v); - - return v == 1; -} - -} - -extern "C" -{ -MATADOR_SQLITE_API matador::sql::connection_impl *create_database(const matador::sql::connection_info &info) -{ - return new matador::backends::sqlite::sqlite_connection(info); -} - -MATADOR_SQLITE_API void destroy_database(matador::sql::connection_impl *db) -{ - delete db; -} - -} diff --git a/backends/sqlite/src/sqlite_dialect.cpp b/backends/sqlite/src/sqlite_dialect.cpp deleted file mode 100644 index 74f421b..0000000 --- a/backends/sqlite/src/sqlite_dialect.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "sqlite_dialect.hpp" - -#include "matador/sql/dialect_builder.hpp" - -[[maybe_unused]] const matador::sql::dialect *get_dialect() -{ - using namespace matador::sql; - const static dialect d = dialect_builder::builder() - .create() - .with_token_replace_map({ - {dialect::token_t::BEGIN, "BEGIN TRANSACTION"}, - {dialect::token_t::COMMIT, "COMMIT TRANSACTION"}, - {dialect::token_t::ROLLBACK, "ROLLBACK TRANSACTION"} - }) - .with_default_schema_name("main") - .build(); - - return &d; -} diff --git a/backends/sqlite/src/sqlite_error.cpp b/backends/sqlite/src/sqlite_error.cpp deleted file mode 100644 index b190f3a..0000000 --- a/backends/sqlite/src/sqlite_error.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "sqlite_error.hpp" - -#include -#include - -#include - -namespace matador::backends::sqlite { - -void throw_sqlite_error(int ec, sqlite3 *db, const std::string &source) -{ - if (ec != SQLITE_OK && ec != SQLITE_DONE) { - std::stringstream msg; - msg << "sqlite error (" << source << "): " << sqlite3_errmsg(db); - throw std::logic_error(msg.str()); - } -} - -void throw_sqlite_error(int ec, sqlite3 *db, const std::string &source, const std::string &sql) -{ - if (ec != SQLITE_OK&& ec != SQLITE_DONE) { - std::stringstream msg; - msg << "sqlite error (" << source << ", sql: " << sql << "): " << sqlite3_errmsg(db); - throw std::logic_error(msg.str()); - } -} - -} \ No newline at end of file diff --git a/backends/sqlite/src/sqlite_parameter_binder.cpp b/backends/sqlite/src/sqlite_parameter_binder.cpp deleted file mode 100644 index 98f278c..0000000 --- a/backends/sqlite/src/sqlite_parameter_binder.cpp +++ /dev/null @@ -1,126 +0,0 @@ -#include "sqlite_parameter_binder.h" -#include "sqlite_error.hpp" - -namespace matador::backends::sqlite { - -sqlite_parameter_binder::sqlite_parameter_binder(sqlite3 *db, sqlite3_stmt *stmt) -: db_(db) -, stmt_(stmt) -{} - -void sqlite_parameter_binder::bind(size_t pos, char i) -{ - int ret = sqlite3_bind_int(stmt_, static_cast(++pos), i); - throw_sqlite_error(ret, db_, "sqlite3_bind_int"); -} - -void sqlite_parameter_binder::bind(size_t pos, short i) -{ - int ret = sqlite3_bind_int(stmt_, static_cast(++pos), i); - throw_sqlite_error(ret, db_, "sqlite3_bind_int"); -} - -void sqlite_parameter_binder::bind(size_t pos, int i) -{ - int ret = sqlite3_bind_int(stmt_, static_cast(++pos), i); - throw_sqlite_error(ret, db_, "sqlite3_bind_int"); -} - -void sqlite_parameter_binder::bind(size_t pos, long i) -{ - int ret = sqlite3_bind_int(stmt_, static_cast(++pos), i); - throw_sqlite_error(ret, db_, "sqlite3_bind_int"); -} - -void sqlite_parameter_binder::bind(size_t pos, long long int i) -{ - int ret = sqlite3_bind_int64(stmt_, static_cast(++pos), i); - throw_sqlite_error(ret, db_, "sqlite3_bind_int"); -} - -void sqlite_parameter_binder::bind(size_t pos, unsigned char i) -{ - int ret = sqlite3_bind_int(stmt_, static_cast(++pos), i); - throw_sqlite_error(ret, db_, "sqlite3_bind_int"); -} - -void sqlite_parameter_binder::bind(size_t pos, unsigned short i) -{ - int ret = sqlite3_bind_int(stmt_, static_cast(++pos), i); - throw_sqlite_error(ret, db_, "sqlite3_bind_int"); -} - -void sqlite_parameter_binder::bind(size_t pos, unsigned int i) -{ - int ret = sqlite3_bind_int64(stmt_, static_cast(++pos), i); - throw_sqlite_error(ret, db_, "sqlite3_bind_int"); -} - -void sqlite_parameter_binder::bind(size_t pos, unsigned long i) -{ - int ret = sqlite3_bind_int64(stmt_, static_cast(++pos), i); - throw_sqlite_error(ret, db_, "sqlite3_bind_int"); -} - -void sqlite_parameter_binder::bind(size_t pos, unsigned long long int i) -{ - int ret = sqlite3_bind_int64(stmt_, static_cast(++pos), i); - throw_sqlite_error(ret, db_, "sqlite3_bind_int"); -} - -void sqlite_parameter_binder::bind(size_t pos, bool b) -{ - int ret = sqlite3_bind_int(stmt_, static_cast(++pos), b); - throw_sqlite_error(ret, db_, "sqlite3_bind_int"); -} - -void sqlite_parameter_binder::bind(size_t pos, float d) -{ - int ret = sqlite3_bind_double(stmt_, static_cast(++pos), d); - throw_sqlite_error(ret, db_, "sqlite3_bind_int"); -} - -void sqlite_parameter_binder::bind(size_t pos, double d) -{ - int ret = sqlite3_bind_double(stmt_, static_cast(++pos), d); - throw_sqlite_error(ret, db_, "sqlite3_bind_int"); -} - -void sqlite_parameter_binder::bind(size_t pos, const char *str) -{ - int ret = sqlite3_bind_text(stmt_, static_cast(++pos), str, static_cast(strlen(str)), nullptr); - throw_sqlite_error(ret, db_, "sqlite3_bind_text"); -} - -void sqlite_parameter_binder::bind(size_t pos, const char *x, size_t size) -{ - auto len = strlen(x); - size = (len > size) ? size : len; - int ret = sqlite3_bind_text(stmt_, static_cast(++pos), x, static_cast(size), nullptr); - throw_sqlite_error(ret, db_, "sqlite3_bind_text"); -} - -void sqlite_parameter_binder::bind(size_t pos, const std::string &str) -{ - int ret = sqlite3_bind_text(stmt_, static_cast(++pos), str.c_str(), static_cast(str.size()), nullptr); - throw_sqlite_error(ret, db_, "sqlite3_bind_text"); -} - -void sqlite_parameter_binder::bind(size_t pos, const std::string &x, size_t size) -{ - auto len = x.size(); - if (size == 0) { - size = len; - } else { - size = (len > size) ? size : len; - } - int ret = sqlite3_bind_text(stmt_, static_cast(++pos), x.data(), static_cast(size), nullptr); - throw_sqlite_error(ret, db_, "sqlite3_bind_text"); -} - -void sqlite_parameter_binder::bind(size_t pos, const utils::blob &blob) -{ - -} - -} \ No newline at end of file diff --git a/backends/sqlite/src/sqlite_prepared_result_reader.cpp b/backends/sqlite/src/sqlite_prepared_result_reader.cpp deleted file mode 100644 index ba6d528..0000000 --- a/backends/sqlite/src/sqlite_prepared_result_reader.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "sqlite_prepared_result_reader.hpp" -#include "sqlite_error.hpp" - -namespace matador::backends::sqlite { - -sqlite_prepared_result_reader::sqlite_prepared_result_reader(sqlite3 *db, sqlite3_stmt *stmt) -: db_(db) -, stmt_(stmt) -{} - -size_t sqlite_prepared_result_reader::column_count() const -{ - return sqlite3_column_count(stmt_); -} - -const char *sqlite_prepared_result_reader::column(size_t index) const -{ - return reinterpret_cast(sqlite3_column_text(stmt_, static_cast(index))); -} - -bool sqlite_prepared_result_reader::fetch() -{ - int ret = sqlite3_step(stmt_); - if (ret != SQLITE_ROW) { - throw_sqlite_error(ret, db_, "sqlite3_step"); - } - return ret != SQLITE_DONE; -} - -void sqlite_prepared_result_reader::read_value(const char *id, size_t index, char &value) -{ - value = static_cast(sqlite3_column_int(stmt_, static_cast(index))); -} - -void sqlite_prepared_result_reader::read_value(const char *id, size_t index, short &value) -{ - query_result_reader::read_value(id, index, value); -} - -void sqlite_prepared_result_reader::read_value(const char *id, size_t index, int &value) -{ - query_result_reader::read_value(id, index, value); -} - -void sqlite_prepared_result_reader::read_value(const char *id, size_t index, long &value) -{ - query_result_reader::read_value(id, index, value); -} - -void sqlite_prepared_result_reader::read_value(const char *id, size_t index, long long int &value) -{ - query_result_reader::read_value(id, index, value); -} - -void sqlite_prepared_result_reader::read_value(const char *id, size_t index, unsigned char &value) -{ - query_result_reader::read_value(id, index, value); -} - -void sqlite_prepared_result_reader::read_value(const char *id, size_t index, unsigned short &value) -{ - query_result_reader::read_value(id, index, value); -} - -void sqlite_prepared_result_reader::read_value(const char *id, size_t index, unsigned int &value) -{ - query_result_reader::read_value(id, index, value); -} - -void sqlite_prepared_result_reader::read_value(const char *id, size_t index, unsigned long &value) -{ - query_result_reader::read_value(id, index, value); -} - -void sqlite_prepared_result_reader::read_value(const char *id, size_t index, unsigned long long int &value) -{ - query_result_reader::read_value(id, index, value); -} - -void sqlite_prepared_result_reader::read_value(const char *id, size_t index, bool &value) -{ - query_result_reader::read_value(id, index, value); -} - -void sqlite_prepared_result_reader::read_value(const char *id, size_t index, float &value) -{ - query_result_reader::read_value(id, index, value); -} - -void sqlite_prepared_result_reader::read_value(const char *id, size_t index, double &value) -{ - query_result_reader::read_value(id, index, value); -} - -void sqlite_prepared_result_reader::read_value(const char *id, size_t index, char *value, size_t s) -{ - query_result_reader::read_value(id, index, value, s); -} - -void sqlite_prepared_result_reader::read_value(const char *id, size_t index, std::string &value) -{ - query_result_reader::read_value(id, index, value); -} - -void sqlite_prepared_result_reader::read_value(const char *id, size_t index, std::string &value, size_t s) -{ - query_result_reader::read_value(id, index, value, s); -} - -void sqlite_prepared_result_reader::read_value(const char *id, size_t index, sql::value &val, size_t size) -{ - query_result_reader::read_value(id, index, val, size); -} -} \ No newline at end of file diff --git a/backends/sqlite/src/sqlite_result_reader.cpp b/backends/sqlite/src/sqlite_result_reader.cpp deleted file mode 100644 index b62fe52..0000000 --- a/backends/sqlite/src/sqlite_result_reader.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "sqlite_result_reader.hpp" - -#include - -namespace matador::backends::sqlite { - -sqlite_result_reader::sqlite_result_reader(sqlite_result_reader::rows result, size_t column_count) -: result_(std::move(result)) -, column_count_(column_count) {} - -sqlite_result_reader::~sqlite_result_reader() -{ - std::for_each(result_.begin(), result_.end(), [](rows ::value_type& row) { - std::for_each(row.begin(), row.end(), [](const char *val) { - delete [] val; - }); - }); -} - -size_t sqlite_result_reader::column_count() const -{ - return column_count_; -} - -const char* sqlite_result_reader::column(size_t index) const -{ - return result_[row_index_][index]; -} - -bool sqlite_result_reader::fetch() -{ - return ++row_index_ < result_.size(); -} - -} \ No newline at end of file diff --git a/backends/sqlite/src/sqlite_statement.cpp b/backends/sqlite/src/sqlite_statement.cpp deleted file mode 100644 index e73e91c..0000000 --- a/backends/sqlite/src/sqlite_statement.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "sqlite_statement.hpp" -#include "sqlite_prepared_result_reader.hpp" -#include "sqlite_error.hpp" - -namespace matador::backends::sqlite { -sqlite_statement::sqlite_statement(sqlite3 *db, sqlite3_stmt *stmt, const sql::query_context &query) -: statement_impl(query) -, db_(db) -, stmt_(stmt) -, binder_(db, stmt) -{} - -sqlite_statement::~sqlite_statement() -{ - sqlite3_finalize(stmt_); -} - -size_t sqlite_statement::execute() -{ - // get next row - int ret = sqlite3_reset(stmt_); - throw_sqlite_error(ret, db_, "sqlite3_reset"); - if (ret = sqlite3_step(stmt_); ret != SQLITE_DONE) { - throw_sqlite_error(ret, db_, "sqlite3_step"); - } - - return sqlite3_changes(db_); -} - -std::unique_ptr sqlite_statement::fetch() -{ - int ret = sqlite3_reset(stmt_); - throw_sqlite_error(ret, db_, "sqlite3_reset"); - - auto reader = std::make_unique(db_, stmt_); - return std::move(std::make_unique(std::move(reader), query_.prototype)); -} - -void sqlite_statement::reset() -{ - if (stmt_) { - sqlite3_reset(stmt_); - sqlite3_clear_bindings(stmt_); - } -} - -sql::parameter_binder& sqlite_statement::binder() -{ - return binder_; -} - -} \ No newline at end of file diff --git a/backends/sqlite/test/CMakeLists.txt b/backends/sqlite/test/CMakeLists.txt deleted file mode 100644 index 284a284..0000000 --- a/backends/sqlite/test/CMakeLists.txt +++ /dev/null @@ -1,46 +0,0 @@ -Include(FetchContent) - -FetchContent_Declare( - Catch2 - GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v3.5.4 # or a later release -) - -FetchContent_MakeAvailable(Catch2) - -list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) -include(CTest) -include(Catch) - -set(SQLITE_CONNECTION_STRING "sqlite://test.db") - -configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/sqlite/test/connection.hpp @ONLY IMMEDIATE) - -message(STATUS "sqlite connection string: ${SQLITE_CONNECTION_STRING}") - -set(TEST_SOURCES - ../../tests/QueryTest.cpp - ../../tests/QueryTest.cpp - ../../tests/ConnectionTest.cpp - ../../tests/QueryRecordTest.cpp - ../../tests/StatementTest.cpp - ../../tests/TypeTraitsTest.cpp - ../../tests/StatementCacheTest.cpp - ../../tests/SessionTest.cpp) - -set(LIBRARY_TEST_TARGET sqlite_tests) - -add_executable(${LIBRARY_TEST_TARGET} ${TEST_SOURCES}) - -target_link_libraries(${LIBRARY_TEST_TARGET} PRIVATE - Catch2::Catch2WithMain - matador - ${CMAKE_DL_LIBS} - ${SQLite3_LIBRARY}) - -target_include_directories(${LIBRARY_TEST_TARGET} - PUBLIC $/include - PRIVATE $/test - PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) - -catch_discover_tests(${LIBRARY_TEST_TARGET} TEST_SUFFIX " (SQLite3)") diff --git a/backends/sqlite/test/Connection.hpp.in b/backends/sqlite/test/Connection.hpp.in deleted file mode 100644 index 577ac3e..0000000 --- a/backends/sqlite/test/Connection.hpp.in +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef SQLITE_CONNECTION_HPP -#define SQLITE_CONNECTION_HPP - -namespace matador::test::connection { - const char* const dns = "@SQLITE_CONNECTION_STRING@"; -} - -#endif /*SQLITE_CONNECTION_HPP*/ \ No newline at end of file diff --git a/backends/tests/QueryRecordTest.cpp b/backends/tests/QueryRecordTest.cpp deleted file mode 100644 index 933b30d..0000000 --- a/backends/tests/QueryRecordTest.cpp +++ /dev/null @@ -1,402 +0,0 @@ -#include - -#include "matador/sql/column.hpp" -#include "matador/sql/condition.hpp" -#include "matador/sql/connection.hpp" -#include "matador/sql/query_builder.hpp" - -#include "connection.hpp" - -#include - -class QueryRecordFixture -{ -public: - QueryRecordFixture() - : db(matador::test::connection::dns) - , schema(db.dialect().default_schema_name()) - { - db.open(); - } - ~QueryRecordFixture() { - drop_table_if_exists("flight"); - drop_table_if_exists("airplane"); - drop_table_if_exists("person"); - drop_table_if_exists("quotes"); - } - -protected: - matador::sql::connection db; - matador::sql::schema schema; - -private: - void drop_table_if_exists(const std::string &table_name) { - if (db.exists(table_name)) { - db.query(schema).drop().table(table_name).execute(); - } - } -}; - -using namespace matador::sql; - -TEST_CASE_METHOD(QueryRecordFixture, "Create and drop table statement", "[session][record]") -{ - REQUIRE(!db.exists("person")); - db.query(schema).create() - .table("person", { - make_pk_column("id"), - make_column("name", 255), - make_column("age") - }) - .execute(); - - REQUIRE(db.exists("person")); - - db.query(schema).drop() - .table("person") - .execute(); - - REQUIRE(!db.exists("person")); -} - -TEST_CASE_METHOD(QueryRecordFixture, "Create and drop table statement with foreign key", "[session][record]") -{ - db.query(schema).create() - .table("airplane", { - make_pk_column("id"), - make_column("brand", 255), - make_column("model", 255), - }) - .execute(); - - REQUIRE(db.exists("airplane")); - - db.query(schema).create() - .table("flight", { - make_pk_column("id"), - make_fk_column("airplane_id", "airplane", "id"), - make_column("pilot_name", 255), - }) - .execute(); - - REQUIRE(db.exists("flight")); - - db.query(schema).drop() - .table("flight") - .execute(); - - REQUIRE(!db.exists("flight")); - - db.query(schema).drop() - .table("airplane") - .execute(); - - REQUIRE(!db.exists("airplane")); -} - -TEST_CASE_METHOD(QueryRecordFixture, "Execute insert record statement", "[session][record]") -{ - db.query(schema).create() - .table("person", { - make_pk_column("id"), - make_column("name", 255), - make_column("age") - }) - .execute(); - - auto res = db.query(schema).insert() - .into("person", {"id", "name", "age"}) - .values({7, "george", 45}) - .execute(); - - REQUIRE(res == 1); - - auto result = db.query(schema).select({"id", "name", "age"}) - .from("person") - .fetch_all(); - - for (const auto &i: result) { - REQUIRE(i.size() == 3); - REQUIRE(i.at(0).name() == "id"); - // REQUIRE(i.at(0).type() == data_type_t::type_long_long); - REQUIRE(i.at(0).template as() == 7); - REQUIRE(i.at(1).name() == "name"); - // REQUIRE(i.at(1).type() == data_type_t::type_varchar); - REQUIRE(i.at(1).template as() == "george"); - REQUIRE(i.at(2).name() == "age"); - // REQUIRE(i.at(2).type() == matador::sql::data_type_t::type_int); - REQUIRE(i.at(2).template as() == 45); - } - - db.query(schema).drop() - .table("person") - .execute(); -} - -TEST_CASE_METHOD(QueryRecordFixture, "Execute insert record statement with foreign key", "[session][record]") -{ - db.query(schema).create() - .table("airplane", { - make_pk_column("id"), - make_column("brand", 255), - make_column("model", 255), - }) - .execute(); - - db.query(schema).create() - .table("flight", { - make_pk_column("id"), - make_fk_column("airplane_id", "airplane", "id"), - make_column("pilot_name", 255), - }) - .execute(); - - auto res = db.query(schema).insert().into("airplane", {"id", "brand", "model"}).values({1, "Airbus", "A380"}).execute(); - REQUIRE(res == 1); - - res = db.query(schema).insert().into("airplane", {"id", "brand", "model"}).values({2, "Boeing", "707"}).execute(); - REQUIRE(res == 1); - - res = db.query(schema).insert().into("airplane", {"id", "brand", "model"}).values({3, "Boeing", "747"}).execute(); - REQUIRE(res == 1); - - auto count = db.query(schema).select({count_all()}).from("airplane").fetch_value(); - REQUIRE(count == 3); - - res = db.query(schema).insert().into("flight", {"id", "airplane_id", "pilot_name"}).values({4, 1, "George"}).execute(); - REQUIRE(res == 1); - - db.query(schema).drop().table("flight").execute(); - db.query(schema).drop().table("airplane").execute(); - - REQUIRE(!db.exists("flight")); - REQUIRE(!db.exists("airplane")); -} - -TEST_CASE_METHOD(QueryRecordFixture, "Execute update record statement", "[session][record]") -{ - db.query(schema).create() - .table("person", { - make_pk_column("id"), - make_column("name", 255), - make_column("age") - }) - .execute(); - - auto res = db.query(schema).insert() - .into("person", {"id", "name", "age"}) - .values({7, "george", 45}) - .execute(); - - REQUIRE(res == 1); - - res = db.query(schema).update("person") - .set({{"id", 7}, - {"name", "jane"}, - {"age", 35}}) - .where("id"_col == 7) - .execute(); - - REQUIRE(res == 1); - - auto result = db.query(schema).select({"id", "name", "age"}) - .from("person") - .fetch_all(); - - for (const auto &i: result) { - REQUIRE(i.size() == 3); - REQUIRE(i.at(0).name() == "id"); - REQUIRE(i.at(0).is_integer()); - REQUIRE(i.at(0).as() == 7); - REQUIRE(i.at(1).name() == "name"); - REQUIRE(i.at(1).is_varchar()); - REQUIRE(i.at(1).as() == "jane"); - REQUIRE(i.at(2).name() == "age"); - REQUIRE(i.at(2).is_integer()); - REQUIRE(i.at(2).as() == 35); - } - - db.query(schema).drop().table("person").execute(); -} - -TEST_CASE_METHOD(QueryRecordFixture, "Execute select statement", "[session][record]") -{ - db.query(schema).create() - .table("person", { - make_pk_column("id"), - make_column("name", 255), - make_column("age") - }) - .execute(); - - auto res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute(); - REQUIRE(res == 1); - res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({2, "jane", 32}).execute(); - REQUIRE(res == 1); - res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({3, "michael", 67}).execute(); - REQUIRE(res == 1); - res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({4, "bob", 13}).execute(); - REQUIRE(res == 1); - - auto result = db.query(schema).select({"id", "name", "age"}) - .from("person") - .fetch_all(); - - std::list expected_names{"george", "jane", "michael", "bob"}; - for (const auto &p: result) { - REQUIRE(p.at(1).str() == expected_names.front()); - expected_names.pop_front(); - } - REQUIRE(expected_names.empty()); - - auto rec = db.query(schema).select({"id", "name", "age"}) - .from("person") - .fetch_one(); - REQUIRE(rec.has_value()); - REQUIRE(rec->at(1).str() == "george"); - - auto name = db.query(schema).select({"name"}) - .from("person") - .fetch_value(); - REQUIRE(name == "george"); - - db.query(schema).drop().table("person").execute(); -} - -TEST_CASE_METHOD(QueryRecordFixture, "Execute select statement with order by", "[session][record]") -{ - db.query(schema).create() - .table("person", { - make_pk_column("id"), - make_column("name", 255), - make_column("age") - }) - .execute(); - - auto res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute(); - REQUIRE(res == 1); - res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({2, "jane", 32}).execute(); - REQUIRE(res == 1); - res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({3, "michael", 67}).execute(); - REQUIRE(res == 1); - res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({4, "bob", 13}).execute(); - REQUIRE(res == 1); - - auto result = db.query(schema).select({"id", "name", "age"}) - .from("person") - .order_by("name").asc() - .fetch_all(); - - std::list expected_names{"bob", "george", "jane", "michael"}; - for (const auto &p: result) { - REQUIRE(p.at(1).str() == expected_names.front()); - expected_names.pop_front(); - } - REQUIRE(expected_names.empty()); - - db.query(schema).drop().table("person").execute(); -} - -TEST_CASE_METHOD(QueryRecordFixture, "Execute select statement with group by and order by", "[session][record]") -{ - db.query(schema).create() - .table("person", { - make_pk_column("id"), - make_column("name", 255), - make_column("age") - }) - .execute(); - - auto res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute(); - REQUIRE(res == 1); - res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({2, "jane", 45}).execute(); - REQUIRE(res == 1); - res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({3, "michael", 13}).execute(); - REQUIRE(res == 1); - res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({4, "bob", 13}).execute(); - REQUIRE(res == 1); - res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({5, "charlie", 67}).execute(); - REQUIRE(res == 1); - - auto result = db.query(schema).select({count("age").as("age_count"), "age"}) - .from("person") - .group_by("age") - .order_by("age_count").desc() - .fetch_all(); - - std::list> expected_values{{2, 45}, - {2, 13}, - {1, 67}}; - for (const auto &r: result) { - REQUIRE(r.at(0).as() == expected_values.front().first); - REQUIRE(r.at(1).as() == expected_values.front().second); - expected_values.pop_front(); - } - - db.query(schema).drop().table("person").execute(); -} - -TEST_CASE_METHOD(QueryRecordFixture, "Execute delete statement", "[session][record]") -{ - db.query(schema).create() - .table("person", { - make_pk_column("id"), - make_column("name", 255), - make_column("age") - }).execute(); - - auto res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({1, "george", 45}).execute(); - REQUIRE(res == 1); - res = db.query(schema).insert().into("person", {"id", "name", "age"}).values({2, "jane", 45}).execute(); - REQUIRE(res == 1); - - auto count = db.query(schema).select({count_all()}).from("person").fetch_value(); - REQUIRE(count == 2); - - res = db.query(schema).remove() - .from("person") - .where("id"_col == 1) - .execute(); - - REQUIRE(res == 1); - - count = db.query(schema).select({count_all()}).from("person").fetch_value(); - REQUIRE(count == 1); - - db.query(schema).drop().table("person").execute(); -} - -TEST_CASE_METHOD(QueryRecordFixture, "Test quoted identifier", "[session][record]") { - db.query(schema).create() - .table("quotes", { - make_column("from", 255), - make_column("to", 255) - }).execute(); - - // check table description - std::vector columns = { "from", "to"}; - std::vector types = {data_type_t::type_varchar, data_type_t::type_varchar}; - auto fields = db.describe("quotes"); - - for (const auto &field : fields) { - REQUIRE(field.name() == columns[field.index()]); - REQUIRE(field.type() == types[field.index()]); - } - - db.query(schema).insert().into("quotes", {"from", "to"}).values({"Berlin", "London"}).execute(); - - auto res = db.query(schema).select({"from", "to"}).from("quotes").fetch_one(); - - REQUIRE(res.has_value()); - REQUIRE("Berlin" == res->at("from").str()); - REQUIRE("London" == res->at("to").str()); - - db.query(schema).update("quotes").set({{"from", "Hamburg"}, {"to", "New York"}}).where("from"_col == "Berlin").execute(); - - res = db.query(schema).select({"from", "to"}).from("quotes").fetch_one(); - - REQUIRE("Hamburg" == res->at("from").str()); - REQUIRE("New York" == res->at("to").str()); - - db.query(schema).drop().table("quotes").execute(); -} \ No newline at end of file diff --git a/backends/tests/QueryTest.cpp b/backends/tests/QueryTest.cpp deleted file mode 100644 index 1d65ca2..0000000 --- a/backends/tests/QueryTest.cpp +++ /dev/null @@ -1,535 +0,0 @@ -#include "catch2/catch_test_macros.hpp" - -#include "matador/sql/column_definition.hpp" -#include "matador/sql/condition.hpp" -#include "matador/sql/query_builder.hpp" -#include "matador/sql/session.hpp" - -#include "connection.hpp" - -#include "models/airplane.hpp" -#include "models/flight.hpp" -#include "models/person.hpp" -#include "models/recipe.hpp" - -#include - -using namespace matador::sql; -using namespace matador::test; - -class QueryFixture -{ -public: - QueryFixture() - : db(matador::test::connection::dns) - , schema(db.dialect().default_schema_name()) - { - db.open(); - } - - ~QueryFixture() - { - drop_table_if_exists("flight"); - drop_table_if_exists("airplane"); - drop_table_if_exists("person"); - drop_table_if_exists("recipe_ingredients"); - drop_table_if_exists("recipes"); - drop_table_if_exists("ingredients"); - } - -protected: - matador::sql::connection db; - matador::sql::schema schema; - -private: - void drop_table_if_exists(const std::string &table_name) - { - if (db.exists(table_name)) { - db.query(schema).drop().table(table_name).execute(); - } - } -}; - -TEST_CASE_METHOD(QueryFixture, "Create table with foreign key relation", "[session]") -{ - schema.attach("airplane"); - schema.attach("flight"); - db.query(schema).create() - .table("airplane") - .execute(); - - REQUIRE(db.exists("airplane")); - - db.query(schema).create() - .table("flight") - .execute(); - - REQUIRE(db.exists("flight")); - - db.query(schema).drop().table("flight").execute(); - db.query(schema).drop().table("airplane").execute(); - - REQUIRE(!db.exists("flight")); - REQUIRE(!db.exists("airplane")); -} - -TEST_CASE_METHOD(QueryFixture, "Execute select statement with where clause", "[session]") -{ - schema.attach("person"); - db.query(schema).create() - .table("person") - .execute(); - - person george{7, "george", 45}; - george.image.push_back(37); - - auto res = db.query(schema) - .insert() - .into("person", column_generator::generate(schema, true)) - .values(george) - .execute(); - REQUIRE(res == 1); - - // fetch person as record - auto result_record = db.query(schema) - .select(column_generator::generate(schema, true)) - .from("person") - .where("id"_col == 7) - .fetch_all(); - - for (const auto &i: result_record) { - REQUIRE(i.size() == 4); - REQUIRE(i.at(0).name() == "id"); - REQUIRE(i.at(0).is_integer()); - REQUIRE(i.at(0).as() == george.id); - REQUIRE(i.at(1).name() == "name"); - REQUIRE(i.at(1).is_varchar()); - REQUIRE(i.at(1).as() == george.name); - REQUIRE(i.at(2).name() == "age"); - REQUIRE(i.at(2).is_integer()); - REQUIRE(i.at(2).as() == george.age); - } - - // fetch person as person - auto result_person = db.query(schema) - .select(column_generator::generate(schema, true)) - .from("person") - .where("id"_col == 7) - .fetch_all(); - - for (const auto &i: result_person) { - REQUIRE(i.id == 7); - REQUIRE(i.name == "george"); - REQUIRE(i.age == 45); - } - - db.query(schema).drop().table("person").execute(); -} - -TEST_CASE_METHOD(QueryFixture, "Execute insert statement", "[session]") -{ - db.query(schema).create() - .table("person", { - make_pk_column("id"), - make_column("name", 255), - make_column("color", 63) - }) - .execute(); - - auto res = db.query(schema).insert() - .into("person", {{"", "id", ""}, {"", "name", ""}, {"", "color", ""}}) - .values({7, "george", "green"}) - .execute(); - - REQUIRE(res == 1); - - // fetch person as record - auto result_record = db.query(schema).select({"id", "name", "color"}) - .from("person") - .where("id"_col == 7) - .fetch_all(); - - for (const auto &i: result_record) { - REQUIRE(i.size() == 3); - REQUIRE(i.at(0).name() == "id"); - REQUIRE(i.at(0).is_integer()); - REQUIRE(i.at(0).as() == 7); - REQUIRE(i.at(1).name() == "name"); - REQUIRE(i.at(1).is_varchar()); - REQUIRE(i.at(1).as() == "george"); - REQUIRE(i.at(2).name() == "color"); - REQUIRE(i.at(2).is_varchar()); - REQUIRE(i.at(2).as() == "green"); - } - - db.query(schema).drop().table("person").execute(); -} - -TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key", "[session]") -{ - schema.attach("airplane"); - schema.attach("flight"); - db.query(schema).create() - .table("airplane") - .execute(); - - db.query(schema).create() - .table("flight") - .execute(); - - std::vector> planes{ - make_entity(1, "Airbus", "A380"), - make_entity(2, "Boeing", "707"), - make_entity(3, "Boeing", "747") - }; - - for (const auto &plane: planes) { - auto res = db.query(schema) - .insert() - .into("airplane", column_generator::generate(schema, true)) - .values(*plane) - .execute(); - REQUIRE(res == 1); - } - - auto count = db.query(schema) - .select({count_all()}) - .from("airplane") - .fetch_value().value(); - REQUIRE(count == 3); - - flight f4711{4, planes.at(1), "hans"}; - - auto res = db.query(schema) - .insert() - .into("flight", column_generator::generate(schema, true)) - .values(f4711) - .execute(); - REQUIRE(res == 1); - - auto f = *db.query(schema) - .select(column_generator::generate(schema, true)) - .from("flight") - .fetch_all().begin(); - REQUIRE(f.at(0).as() == 4); - REQUIRE(f.at(1).as() == 2); - REQUIRE(f.at(2).as() == "hans"); - - db.query(schema).drop().table("flight").execute(); - db.query(schema).drop().table("airplane").execute(); -} - -TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left", "[session][join_left]") -{ - schema.attach("airplane"); - schema.attach("flight"); - db.query(schema).create() - .table("airplane") - .execute(); - - db.query(schema).create() - .table("flight") - .execute(); - - std::vector> planes{ - make_entity(1, "Airbus", "A380"), - make_entity(2, "Boeing", "707"), - make_entity(3, "Boeing", "747") - }; - - for (const auto &plane: planes) { - auto res = db.query(schema) - .insert() - .into("airplane", column_generator::generate(schema, true)) - .values(*plane) - .execute(); - REQUIRE(res == 1); - } - - auto count = db.query(schema) - .select({count_all()}) - .from("airplane") - .fetch_value().value(); - REQUIRE(count == 3); - - std::vector> flights{ - make_entity(4, planes.at(0), "hans"), - make_entity(5, planes.at(0), "otto"), - make_entity(6, planes.at(1), "george"), - make_entity(7, planes.at(2), "paul") - }; - - for (const auto &f: flights) { - auto res = db.query(schema) - .insert() - .into("flight", {"id", "airplane_id", "pilot_name"}) - .values(*f) - .execute(); - REQUIRE(res == 1); - } - - auto f = *db.query(schema) - .select(column_generator::generate(schema, true)) - .from("flight") - .fetch_all().begin(); - REQUIRE(f.at(0).as() == 4); - REQUIRE(f.at(1).as() == 1); - REQUIRE(f.at(2).as() == "hans"); - - auto result = db.query(schema).select({"f.id", "ap.brand", "ap.model", "f.pilot_name"}) - .from({"flight", "f"}) - .join_left({"airplane", "ap"}) - .on("f.airplane_id"_col == "ap.id"_col) - .order_by("f.id").asc() - .fetch_all(); - - std::vector> expected_result { - {4, "hans"}, - {5, "otto"}, - {6, "george"}, - {7, "paul"} - }; - size_t index{0}; - for (const auto &r: result) { - REQUIRE(r.size() == 4); - REQUIRE(r.at(0).as() == expected_result[index].first); - REQUIRE(r.at(3).as() == expected_result[index++].second); - } - - db.query(schema).drop().table("flight").execute(); - db.query(schema).drop().table("airplane").execute(); -} - -TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single entity", "[session][join_left][find]") { - schema.attach("airplane"); - schema.attach("flight"); - db.query(schema).create() - .table("airplane") - .execute(); - - db.query(schema).create() - .table("flight") - .execute(); - - std::vector> planes{ - make_entity(1, "Airbus", "A380"), - make_entity(2, "Boeing", "707"), - make_entity(3, "Boeing", "747") - }; - - for (const auto &plane: planes) { - auto res = db - .query(schema) - .insert() - .into("airplane", column_generator::generate(schema, true)) - .values(*plane) - .execute(); - REQUIRE(res == 1); - } - - auto count = db - .query(schema) - .select({count_all()}) - .from("airplane") - .fetch_value().value(); - REQUIRE(count == 3); - - std::vector> flights{ - make_entity(4, planes.at(0), "hans"), - make_entity(5, planes.at(0), "otto"), - make_entity(6, planes.at(1), "george"), - make_entity(7, planes.at(2), "paul") - }; - - for (const auto &f: flights) { - auto res = db.query(schema) - .insert() - .into("flight", column_generator::generate(schema, true)) - .values(*f) - .execute(); - REQUIRE(res == 1); - } - - auto f = db.query(schema) - .select(column_generator::generate(schema, true)) - .from("flight") - .fetch_one(); - REQUIRE(f.has_value()); - REQUIRE(f->at(0).as() == 4); - REQUIRE(f->at(1).as() == 1); - REQUIRE(f->at(2).as() == "hans"); - - auto result = db - .query(schema) - .select({"f.id", "f.airplane_id", "ap.brand", "ap.model", "f.pilot_name"}) - .from({"flight", "f"}) - .join_left({"airplane", "ap"}) - .on("f.airplane_id"_col == "ap.id"_col) - .where("f.id"_col == 4) - .fetch_one(); - - auto expected_flight = flights[0]; - - REQUIRE(result); - REQUIRE(result->id == expected_flight->id); - REQUIRE(result->pilot_name == expected_flight->pilot_name); - REQUIRE(result->airplane.get()); - REQUIRE(result->airplane->id == 1); - REQUIRE(result->airplane->model == "A380"); - REQUIRE(result->airplane->brand == "Airbus"); - - db.query(schema).drop().table("flight").execute(); - db.query(schema).drop().table("airplane").execute(); -} - -TEST_CASE_METHOD(QueryFixture, "Select statement with many to many relationship", "[session][join][many_to_many]") { - schema.attach("recipes"); - schema.attach("ingredients"); - schema.attach("recipe_ingredients"); - db.query(schema).create() - .table("recipes") - .execute(); - - db.query(schema).create() - .table("ingredients") - .execute(); - - db.query(schema).create() - .table("recipe_ingredients") - .execute(); - - std::vector ingredients { - {1, "Apple"}, - {2, "Strawberry"}, - {3, "Pineapple"}, - {4, "Sugar"}, - {5, "Flour"}, - {6, "Butter"}, - {7, "Beans"} - }; - - for (const auto &i: ingredients) { - auto res = db - .query(schema) - .insert() - .into("ingredients", column_generator::generate(schema, true)) - .values(i) - .execute(); - REQUIRE(res == 1); - } - - std::vector recipes{ - {7, "Apple Crumble"}, - {8, "Beans Chili"}, - {9, "Fruit Salad"} - }; - - for (const auto &r: recipes) { - auto res = db - .query(schema) - .insert() - .into("recipes", column_generator::generate(schema, true)) - .values(r) - .execute(); - REQUIRE(res == 1); - } - - std::vector> recipe_ingredients { - { 7, 1 }, - { 7, 4 }, - { 7, 5 }, - { 8, 6 }, - { 8, 7 }, - { 9, 1 }, - { 9, 2 }, - { 9, 3 } - }; - - for (const auto &ri: recipe_ingredients) { - auto res = db - .query(schema) - .insert() - .into("recipe_ingredients", column_generator::generate(schema, true)) - .values({ri.first, ri.second}) - .execute(); - REQUIRE(res == 1); - } - - auto result = db - .query(schema) - .select({"r.id", "r.name", "ri.ingredient_id"}) - .from({"recipes", "r"}) - .join_left({"recipe_ingredients", "ri"}) - .on("r.id"_col == "ri.recipe_id"_col) - .fetch_all(); - - std::vector> expected_result_one_join { - {7, "Apple Crumble", 1}, - {7, "Apple Crumble", 4}, - {7, "Apple Crumble", 5}, - {8, "Beans Chili", 6}, - {8, "Beans Chili", 7}, - {9, "Fruit Salad", 1}, - {9, "Fruit Salad", 2}, - {9, "Fruit Salad", 3} - }; - size_t index{0}; - for (const auto &r: result) { - REQUIRE(r.size() == 3); - REQUIRE(r.at(0).as().value() == std::get<0>(expected_result_one_join[index])); - REQUIRE(r.at(1).as().value() == std::get<1>(expected_result_one_join[index])); - REQUIRE(r.at(2).as().value() == std::get<2>(expected_result_one_join[index])); - ++index; - } - - result = db - .query(schema) - .select({"r.id", "r.name", "ri.ingredient_id", "i.name"}) - .from({"recipes", "r"}) - .join_left({"recipe_ingredients", "ri"}).on("r.id"_col == "ri.recipe_id"_col) - .join_left({"ingredients", "i"}).on("ri.ingredient_id"_col == "i.id"_col) - .fetch_all(); - - std::vector> expected_result_two_joins { - {7, "Apple Crumble", 1, "Apple"}, - {7, "Apple Crumble", 4, "Sugar"}, - {7, "Apple Crumble", 5, "Flour"}, - {8, "Beans Chili", 6, "Butter"}, - {8, "Beans Chili", 7, "Beans"}, - {9, "Fruit Salad", 1, "Apple"}, - {9, "Fruit Salad", 2, "Strawberry"}, - {9, "Fruit Salad", 3, "Pineapple"} - }; - index = 0; - for (const auto &r: result) { - REQUIRE(r.size() == 4); - REQUIRE(r.at(0).as().value() == std::get<0>(expected_result_two_joins[index])); - REQUIRE(r.at(1).as().value() == std::get<1>(expected_result_two_joins[index])); - REQUIRE(r.at(2).as().value() == std::get<2>(expected_result_two_joins[index])); - REQUIRE(r.at(3).as().value() == std::get<3>(expected_result_two_joins[index])); - ++index; - } - - result = db - .query(schema) - .select({"r.id", "r.name", "ri.ingredient_id", "i.name"}) - .from({"recipes", "r"}) - .join_left({"recipe_ingredients", "ri"}).on("r.id"_col == "ri.recipe_id"_col) - .join_left({"ingredients", "i"}).on("ri.ingredient_id"_col == "i.id"_col) - .where("r.id"_col == 8) - .fetch_all(); - - index = 3; - for (const auto &r: result) { - REQUIRE(r.size() == 4); - REQUIRE(r.at(0).as().value() == std::get<0>(expected_result_two_joins[index])); - REQUIRE(r.at(1).as().value() == std::get<1>(expected_result_two_joins[index])); - REQUIRE(r.at(2).as().value() == std::get<2>(expected_result_two_joins[index])); - REQUIRE(r.at(3).as().value() == std::get<3>(expected_result_two_joins[index])); - ++index; - } - - db.query(schema).drop().table("recipe_ingredients").execute(); - db.query(schema).drop().table("recipes").execute(); - db.query(schema).drop().table("ingredients").execute(); -} \ No newline at end of file diff --git a/backends/tests/SessionTest.cpp b/backends/tests/SessionTest.cpp deleted file mode 100644 index 4d4830e..0000000 --- a/backends/tests/SessionTest.cpp +++ /dev/null @@ -1,99 +0,0 @@ -#include "catch2/catch_test_macros.hpp" - -#include "connection.hpp" - -#include "matador/sql/session.hpp" - -#include "models/airplane.hpp" -#include "models/flight.hpp" - -class SessionFixture -{ -public: - SessionFixture() - : pool(matador::test::connection::dns, 4) - , ses(pool) - {} - - ~SessionFixture() - { - drop_table_if_exists("flights"); - drop_table_if_exists("airplanes"); - } - -protected: - matador::sql::connection_pool pool; - matador::sql::session ses; - -private: - void drop_table_if_exists(const std::string &table_name) - { - if (ses.table_exists(table_name)) { - ses.drop_table(table_name); - } - } -}; - -using namespace matador; - -TEST_CASE_METHOD(SessionFixture, "Session relation test", "[session][relation]") { - using namespace matador; - ses.attach("airplanes"); - ses.attach("flights"); - ses.create_schema(); - auto plane = ses.insert(1, "Boeing", "A380"); - auto f = ses.insert(2, plane, "sully"); - - auto result = ses.find(2); - REQUIRE(result.is_ok()); -} - -TEST_CASE_METHOD(SessionFixture, "Use session to find object with id", "[session][find]") { - using namespace matador::test; - ses.attach("airplanes"); - ses.create_schema(); - auto a380 = ses.insert(1, "Boeing", "A380"); - - auto result = ses.find(2); - REQUIRE(!result.is_ok()); - REQUIRE((result.err() == sql::session_error::FailedToFindObject)); - - result = ses.find(1); - - REQUIRE(result); - auto read_a380 = result.value(); - REQUIRE(a380->id == read_a380->id); -} - -TEST_CASE_METHOD(SessionFixture, "Use session to find all objects", "[session][find]") { - using namespace matador::test; - ses.attach("airplanes"); - ses.create_schema(); - - std::vector> planes; - planes.emplace_back(new airplane(1, "Airbus", "A380")); - planes.emplace_back(new airplane(2, "Boeing", "707")); - planes.emplace_back(new airplane(3, "Boeing", "747")); - - for (auto &&plane: planes) { - ses.insert(plane.release()); - } - - auto result = ses.find(); - - std::vector> expected_result { - {1, "Airbus", "A380"}, - {2, "Boeing", "707"}, - {3, "Boeing", "747"} - }; - REQUIRE(result); - auto all_planes = result.release(); - size_t index {0}; - for (const auto &i: all_planes) { - REQUIRE(i.id == std::get<0>(expected_result[index])); - REQUIRE(i.brand == std::get<1>(expected_result[index])); - REQUIRE(i.model == std::get<2>(expected_result[index])); - ++index; - } - -} \ No newline at end of file diff --git a/backends/tests/StatementTest.cpp b/backends/tests/StatementTest.cpp deleted file mode 100644 index 029894a..0000000 --- a/backends/tests/StatementTest.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include - -#include "matador/sql/column_definition.hpp" -#include "matador/sql/condition.hpp" -#include "matador/sql/connection.hpp" - -#include "connection.hpp" - -#include "models/airplane.hpp" - -using namespace matador::sql; -using namespace matador::test; - -class StatementTestFixture -{ -public: - StatementTestFixture() - : db(matador::test::connection::dns) - , schema(db.dialect().default_schema_name()) - { - db.open(); - db.query(schema).create().table("airplane").execute(); - } - - ~StatementTestFixture() - { - drop_table_if_exists("airplane"); - } - -protected: - matador::sql::connection db; - matador::sql::schema schema; - - std::vector> planes{ - make_entity(1, "Airbus", "A380"), - make_entity(2, "Boeing", "707"), - make_entity(3, "Boeing", "747") - }; - -private: - void drop_table_if_exists(const std::string &table_name) { - if (db.exists(table_name)) { - db.query(schema).drop().table(table_name).execute(); - } - } - -}; - -TEST_CASE_METHOD(StatementTestFixture, "Create prepared statement", "[statement]") -{ - schema.attach("airplane"); - table ap{"airplane"}; - SECTION("Insert with prepared statement and placeholder") { - auto stmt = db.query(schema).insert() - .into("airplane", column_generator::generate(schema, true)) - .values() - .prepare(); - - for (const auto &plane: planes) { - auto res = stmt.bind(*plane).execute(); - REQUIRE(res == 1); - stmt.reset(); - } - - auto result = db.query(schema) - .select(column_generator::generate(schema, true)) - .from(ap) - .fetch_all(); - - size_t index{0}; - for (const auto &i: result) { - REQUIRE(i.id == planes[index]->id); - REQUIRE(i.brand == planes[index]->brand); - REQUIRE(i.model == planes[index++]->model); - } - } - - SECTION("Select with prepared statement") { - for (const auto &plane: planes) { - auto res = db.query(schema) - .insert() - .into("airplane", column_generator::generate(schema, true)) - .values(*plane) - .execute(); - REQUIRE(res == 1); - } - - auto stmt = db.query(schema) - .select(column_generator::generate(schema, true)) - .from(ap) - .where("brand"_col == _) - .prepare(); - - stmt.bind(0, "Airbus"); - - auto result = stmt.fetch(); - - for (const auto &i: result) { - REQUIRE(i.id == planes[0]->id); - REQUIRE(i.brand == planes[0]->brand); - REQUIRE(i.model == planes[0]->model); - } - - stmt.reset(); - - stmt.bind(0, "Boeing"); - - result = stmt.fetch(); - - size_t index{1}; - for (const auto &i: result) { - REQUIRE(i.id == planes[index]->id); - REQUIRE(i.brand == planes[index]->brand); - REQUIRE(i.model == planes[index++]->model); - } - } -} \ No newline at end of file diff --git a/backends/tests/TypeTraitsTest.cpp b/backends/tests/TypeTraitsTest.cpp deleted file mode 100644 index cd4ae89..0000000 --- a/backends/tests/TypeTraitsTest.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include - -#include "matador/sql/connection.hpp" -#include "matador/sql/column_generator.hpp" - -#include "matador/utils/enum_mapper.hpp" - -#include "connection.hpp" - -#include "models/location.hpp" - -using namespace matador::sql; -using namespace matador::test; - -class TypeTraitsTestFixture -{ -public: - TypeTraitsTestFixture() - : db(matador::test::connection::dns) - , schema(db.dialect().default_schema_name()) - { - db.open(); - db.query(schema).create() - .table("location") - .execute(); - } - - ~TypeTraitsTestFixture() - { - db.query(schema).drop().table("location").execute(); - } - -protected: - matador::sql::connection db; - matador::sql::schema schema; - -private: - void drop_table_if_exists(const std::string &table_name) { - if (db.exists(table_name)) { - db.query(schema).drop().table(table_name).execute(); - } - } -}; - -static const matador::utils::enum_mapper color_enum({ - {Color::Green, "green"}, - {Color::Red, "red"}, - {Color::Blue, "blue"}, - {Color::Yellow, "yellow"}, - {Color::Black, "black"}, - {Color::White, "white"}, - {Color::Brown, "brown"} - }); - -template<> -struct matador::sql::data_type_traits -{ - inline static data_type_t builtin_type(std::size_t size) - { return data_type_traits::builtin_type(size); } - - static void read_value(query_result_reader &reader, const char *id, size_t index, Color &value) - { - std::string enum_string; - reader.read_value(id, index, enum_string, 64); - if (const auto enum_opt = color_enum.to_enum(enum_string)) { - value = enum_opt.value(); - } - } - - static any_type create_value(const Color &value) - { - return color_enum.to_string(value); - } - - static void bind_value(parameter_binder &binder, size_t index, Color &value) - { - binder.bind(index, color_enum.to_string(value)); - } -}; - -TEST_CASE_METHOD(TypeTraitsTestFixture, "Special handling of attributes with type traits", "[typetraits]") -{ - schema.attach("location"); - SECTION("Insert and select with direct execution") { - location loc{1, "center", {1, 2, 3}, Color::Black}; - - auto res = db - .query(schema) - .insert() - .into("location", column_generator::generate(schema, true)) - .values(loc) - .execute(); - REQUIRE(res == 1); - - auto result = db - .query(schema) - .select(column_generator::generate(schema, true)) - .from("location") - .fetch_all(); - - for (const auto &l: result) { - REQUIRE(l.name == "center"); - } - } - - SECTION("Insert and select with prepared statement") { - location loc{1, "center", {1, 2, 3}, Color::Black}; - - auto stmt = db - .query(schema) - .insert() - .into("location", column_generator::generate(schema, true)) - .values() - .prepare(); - - auto res = stmt - .bind(loc) - .execute(); - REQUIRE(res == 1); - - auto result = db - .query(schema) - .select(column_generator::generate(schema, true)) - .from("location") - .fetch_all(); - - for (const auto &l: result) { - REQUIRE(l.name == "center"); - } - } -} \ No newline at end of file diff --git a/cmake/CPM.cmake b/cmake/CPM.cmake new file mode 100644 index 0000000..8269a8b --- /dev/null +++ b/cmake/CPM.cmake @@ -0,0 +1,1269 @@ +# CPM.cmake - CMake's missing package manager +# =========================================== +# See https://github.com/cpm-cmake/CPM.cmake for usage and update instructions. +# +# MIT License +# ----------- +#[[ + Copyright (c) 2019-2023 Lars Melchior and contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +]] + +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + +# Initialize logging prefix +if(NOT CPM_INDENT) + set(CPM_INDENT + "CPM:" + CACHE INTERNAL "" + ) +endif() + +if(NOT COMMAND cpm_message) + function(cpm_message) + message(${ARGV}) + endfunction() +endif() + +set(CURRENT_CPM_VERSION 0.40.2) + +get_filename_component(CPM_CURRENT_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" REALPATH) +if(CPM_DIRECTORY) + if(NOT CPM_DIRECTORY STREQUAL CPM_CURRENT_DIRECTORY) + if(CPM_VERSION VERSION_LESS CURRENT_CPM_VERSION) + message( + AUTHOR_WARNING + "${CPM_INDENT} \ +A dependency is using a more recent CPM version (${CURRENT_CPM_VERSION}) than the current project (${CPM_VERSION}). \ +It is recommended to upgrade CPM to the most recent version. \ +See https://github.com/cpm-cmake/CPM.cmake for more information." + ) + endif() + if(${CMAKE_VERSION} VERSION_LESS "3.17.0") + include(FetchContent) + endif() + return() + endif() + + get_property( + CPM_INITIALIZED GLOBAL "" + PROPERTY CPM_INITIALIZED + SET + ) + if(CPM_INITIALIZED) + return() + endif() +endif() + +if(CURRENT_CPM_VERSION MATCHES "development-version") + message( + WARNING "${CPM_INDENT} Your project is using an unstable development version of CPM.cmake. \ +Please update to a recent release if possible. \ +See https://github.com/cpm-cmake/CPM.cmake for details." + ) +endif() + +set_property(GLOBAL PROPERTY CPM_INITIALIZED true) + +macro(cpm_set_policies) + # the policy allows us to change options without caching + cmake_policy(SET CMP0077 NEW) + set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) + + # the policy allows us to change set(CACHE) without caching + if(POLICY CMP0126) + cmake_policy(SET CMP0126 NEW) + set(CMAKE_POLICY_DEFAULT_CMP0126 NEW) + endif() + + # The policy uses the download time for timestamp, instead of the timestamp in the archive. This + # allows for proper rebuilds when a projects url changes + if(POLICY CMP0135) + cmake_policy(SET CMP0135 NEW) + set(CMAKE_POLICY_DEFAULT_CMP0135 NEW) + endif() + + # treat relative git repository paths as being relative to the parent project's remote + if(POLICY CMP0150) + cmake_policy(SET CMP0150 NEW) + set(CMAKE_POLICY_DEFAULT_CMP0150 NEW) + endif() +endmacro() +cpm_set_policies() + +option(CPM_USE_LOCAL_PACKAGES "Always try to use `find_package` to get dependencies" + $ENV{CPM_USE_LOCAL_PACKAGES} +) +option(CPM_LOCAL_PACKAGES_ONLY "Only use `find_package` to get dependencies" + $ENV{CPM_LOCAL_PACKAGES_ONLY} +) +option(CPM_DOWNLOAD_ALL "Always download dependencies from source" $ENV{CPM_DOWNLOAD_ALL}) +option(CPM_DONT_UPDATE_MODULE_PATH "Don't update the module path to allow using find_package" + $ENV{CPM_DONT_UPDATE_MODULE_PATH} +) +option(CPM_DONT_CREATE_PACKAGE_LOCK "Don't create a package lock file in the binary path" + $ENV{CPM_DONT_CREATE_PACKAGE_LOCK} +) +option(CPM_INCLUDE_ALL_IN_PACKAGE_LOCK + "Add all packages added through CPM.cmake to the package lock" + $ENV{CPM_INCLUDE_ALL_IN_PACKAGE_LOCK} +) +option(CPM_USE_NAMED_CACHE_DIRECTORIES + "Use additional directory of package name in cache on the most nested level." + $ENV{CPM_USE_NAMED_CACHE_DIRECTORIES} +) + +set(CPM_VERSION + ${CURRENT_CPM_VERSION} + CACHE INTERNAL "" +) +set(CPM_DIRECTORY + ${CPM_CURRENT_DIRECTORY} + CACHE INTERNAL "" +) +set(CPM_FILE + ${CMAKE_CURRENT_LIST_FILE} + CACHE INTERNAL "" +) +set(CPM_PACKAGES + "" + CACHE INTERNAL "" +) +set(CPM_DRY_RUN + OFF + CACHE INTERNAL "Don't download or configure dependencies (for testing)" +) + +if(DEFINED ENV{CPM_SOURCE_CACHE}) + set(CPM_SOURCE_CACHE_DEFAULT $ENV{CPM_SOURCE_CACHE}) +else() + set(CPM_SOURCE_CACHE_DEFAULT OFF) +endif() + +set(CPM_SOURCE_CACHE + ${CPM_SOURCE_CACHE_DEFAULT} + CACHE PATH "Directory to download CPM dependencies" +) + +if(NOT CPM_DONT_UPDATE_MODULE_PATH) + set(CPM_MODULE_PATH + "${CMAKE_BINARY_DIR}/CPM_modules" + CACHE INTERNAL "" + ) + # remove old modules + file(REMOVE_RECURSE ${CPM_MODULE_PATH}) + file(MAKE_DIRECTORY ${CPM_MODULE_PATH}) + # locally added CPM modules should override global packages + set(CMAKE_MODULE_PATH "${CPM_MODULE_PATH};${CMAKE_MODULE_PATH}") +endif() + +if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) + set(CPM_PACKAGE_LOCK_FILE + "${CMAKE_BINARY_DIR}/cpm-package-lock.cmake" + CACHE INTERNAL "" + ) + file(WRITE ${CPM_PACKAGE_LOCK_FILE} + "# CPM Package Lock\n# This file should be committed to version control\n\n" + ) +endif() + +include(FetchContent) + +# Try to infer package name from git repository uri (path or url) +function(cpm_package_name_from_git_uri URI RESULT) + if("${URI}" MATCHES "([^/:]+)/?.git/?$") + set(${RESULT} + ${CMAKE_MATCH_1} + PARENT_SCOPE + ) + else() + unset(${RESULT} PARENT_SCOPE) + endif() +endfunction() + +# Try to infer package name and version from a url +function(cpm_package_name_and_ver_from_url url outName outVer) + if(url MATCHES "[/\\?]([a-zA-Z0-9_\\.-]+)\\.(tar|tar\\.gz|tar\\.bz2|zip|ZIP)(\\?|/|$)") + # We matched an archive + set(filename "${CMAKE_MATCH_1}") + + if(filename MATCHES "([a-zA-Z0-9_\\.-]+)[_-]v?(([0-9]+\\.)*[0-9]+[a-zA-Z0-9]*)") + # We matched - (ie foo-1.2.3) + set(${outName} + "${CMAKE_MATCH_1}" + PARENT_SCOPE + ) + set(${outVer} + "${CMAKE_MATCH_2}" + PARENT_SCOPE + ) + elseif(filename MATCHES "(([0-9]+\\.)+[0-9]+[a-zA-Z0-9]*)") + # We couldn't find a name, but we found a version + # + # In many cases (which we don't handle here) the url would look something like + # `irrelevant/ACTUAL_PACKAGE_NAME/irrelevant/1.2.3.zip`. In such a case we can't possibly + # distinguish the package name from the irrelevant bits. Moreover if we try to match the + # package name from the filename, we'd get bogus at best. + unset(${outName} PARENT_SCOPE) + set(${outVer} + "${CMAKE_MATCH_1}" + PARENT_SCOPE + ) + else() + # Boldly assume that the file name is the package name. + # + # Yes, something like `irrelevant/ACTUAL_NAME/irrelevant/download.zip` will ruin our day, but + # such cases should be quite rare. No popular service does this... we think. + set(${outName} + "${filename}" + PARENT_SCOPE + ) + unset(${outVer} PARENT_SCOPE) + endif() + else() + # No ideas yet what to do with non-archives + unset(${outName} PARENT_SCOPE) + unset(${outVer} PARENT_SCOPE) + endif() +endfunction() + +function(cpm_find_package NAME VERSION) + string(REPLACE " " ";" EXTRA_ARGS "${ARGN}") + find_package(${NAME} ${VERSION} ${EXTRA_ARGS} QUIET) + if(${CPM_ARGS_NAME}_FOUND) + if(DEFINED ${CPM_ARGS_NAME}_VERSION) + set(VERSION ${${CPM_ARGS_NAME}_VERSION}) + endif() + cpm_message(STATUS "${CPM_INDENT} Using local package ${CPM_ARGS_NAME}@${VERSION}") + CPMRegisterPackage(${CPM_ARGS_NAME} "${VERSION}") + set(CPM_PACKAGE_FOUND + YES + PARENT_SCOPE + ) + else() + set(CPM_PACKAGE_FOUND + NO + PARENT_SCOPE + ) + endif() +endfunction() + +# Create a custom FindXXX.cmake module for a CPM package This prevents `find_package(NAME)` from +# finding the system library +function(cpm_create_module_file Name) + if(NOT CPM_DONT_UPDATE_MODULE_PATH) + # erase any previous modules + file(WRITE ${CPM_MODULE_PATH}/Find${Name}.cmake + "include(\"${CPM_FILE}\")\n${ARGN}\nset(${Name}_FOUND TRUE)" + ) + endif() +endfunction() + +# Find a package locally or fallback to CPMAddPackage +function(CPMFindPackage) + set(oneValueArgs NAME VERSION GIT_TAG FIND_PACKAGE_ARGUMENTS) + + cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "" ${ARGN}) + + if(NOT DEFINED CPM_ARGS_VERSION) + if(DEFINED CPM_ARGS_GIT_TAG) + cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION) + endif() + endif() + + set(downloadPackage ${CPM_DOWNLOAD_ALL}) + if(DEFINED CPM_DOWNLOAD_${CPM_ARGS_NAME}) + set(downloadPackage ${CPM_DOWNLOAD_${CPM_ARGS_NAME}}) + elseif(DEFINED ENV{CPM_DOWNLOAD_${CPM_ARGS_NAME}}) + set(downloadPackage $ENV{CPM_DOWNLOAD_${CPM_ARGS_NAME}}) + endif() + if(downloadPackage) + CPMAddPackage(${ARGN}) + cpm_export_variables(${CPM_ARGS_NAME}) + return() + endif() + + cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS}) + + if(NOT CPM_PACKAGE_FOUND) + CPMAddPackage(${ARGN}) + cpm_export_variables(${CPM_ARGS_NAME}) + endif() + +endfunction() + +# checks if a package has been added before +function(cpm_check_if_package_already_added CPM_ARGS_NAME CPM_ARGS_VERSION) + if("${CPM_ARGS_NAME}" IN_LIST CPM_PACKAGES) + CPMGetPackageVersion(${CPM_ARGS_NAME} CPM_PACKAGE_VERSION) + if("${CPM_PACKAGE_VERSION}" VERSION_LESS "${CPM_ARGS_VERSION}") + message( + WARNING + "${CPM_INDENT} Requires a newer version of ${CPM_ARGS_NAME} (${CPM_ARGS_VERSION}) than currently included (${CPM_PACKAGE_VERSION})." + ) + endif() + cpm_get_fetch_properties(${CPM_ARGS_NAME}) + set(${CPM_ARGS_NAME}_ADDED NO) + set(CPM_PACKAGE_ALREADY_ADDED + YES + PARENT_SCOPE + ) + cpm_export_variables(${CPM_ARGS_NAME}) + else() + set(CPM_PACKAGE_ALREADY_ADDED + NO + PARENT_SCOPE + ) + endif() +endfunction() + +# Parse the argument of CPMAddPackage in case a single one was provided and convert it to a list of +# arguments which can then be parsed idiomatically. For example gh:foo/bar@1.2.3 will be converted +# to: GITHUB_REPOSITORY;foo/bar;VERSION;1.2.3 +function(cpm_parse_add_package_single_arg arg outArgs) + # Look for a scheme + if("${arg}" MATCHES "^([a-zA-Z]+):(.+)$") + string(TOLOWER "${CMAKE_MATCH_1}" scheme) + set(uri "${CMAKE_MATCH_2}") + + # Check for CPM-specific schemes + if(scheme STREQUAL "gh") + set(out "GITHUB_REPOSITORY;${uri}") + set(packageType "git") + elseif(scheme STREQUAL "gl") + set(out "GITLAB_REPOSITORY;${uri}") + set(packageType "git") + elseif(scheme STREQUAL "bb") + set(out "BITBUCKET_REPOSITORY;${uri}") + set(packageType "git") + # A CPM-specific scheme was not found. Looks like this is a generic URL so try to determine + # type + elseif(arg MATCHES ".git/?(@|#|$)") + set(out "GIT_REPOSITORY;${arg}") + set(packageType "git") + else() + # Fall back to a URL + set(out "URL;${arg}") + set(packageType "archive") + + # We could also check for SVN since FetchContent supports it, but SVN is so rare these days. + # We just won't bother with the additional complexity it will induce in this function. SVN is + # done by multi-arg + endif() + else() + if(arg MATCHES ".git/?(@|#|$)") + set(out "GIT_REPOSITORY;${arg}") + set(packageType "git") + else() + # Give up + message(FATAL_ERROR "${CPM_INDENT} Can't determine package type of '${arg}'") + endif() + endif() + + # For all packages we interpret @... as version. Only replace the last occurrence. Thus URIs + # containing '@' can be used + string(REGEX REPLACE "@([^@]+)$" ";VERSION;\\1" out "${out}") + + # Parse the rest according to package type + if(packageType STREQUAL "git") + # For git repos we interpret #... as a tag or branch or commit hash + string(REGEX REPLACE "#([^#]+)$" ";GIT_TAG;\\1" out "${out}") + elseif(packageType STREQUAL "archive") + # For archives we interpret #... as a URL hash. + string(REGEX REPLACE "#([^#]+)$" ";URL_HASH;\\1" out "${out}") + # We don't try to parse the version if it's not provided explicitly. cpm_get_version_from_url + # should do this at a later point + else() + # We should never get here. This is an assertion and hitting it means there's a problem with the + # code above. A packageType was set, but not handled by this if-else. + message(FATAL_ERROR "${CPM_INDENT} Unsupported package type '${packageType}' of '${arg}'") + endif() + + set(${outArgs} + ${out} + PARENT_SCOPE + ) +endfunction() + +# Check that the working directory for a git repo is clean +function(cpm_check_git_working_dir_is_clean repoPath gitTag isClean) + + find_package(Git REQUIRED) + + if(NOT GIT_EXECUTABLE) + # No git executable, assume directory is clean + set(${isClean} + TRUE + PARENT_SCOPE + ) + return() + endif() + + # check for uncommitted changes + execute_process( + COMMAND ${GIT_EXECUTABLE} status --porcelain + RESULT_VARIABLE resultGitStatus + OUTPUT_VARIABLE repoStatus + OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET + WORKING_DIRECTORY ${repoPath} + ) + if(resultGitStatus) + # not supposed to happen, assume clean anyway + message(WARNING "${CPM_INDENT} Calling git status on folder ${repoPath} failed") + set(${isClean} + TRUE + PARENT_SCOPE + ) + return() + endif() + + if(NOT "${repoStatus}" STREQUAL "") + set(${isClean} + FALSE + PARENT_SCOPE + ) + return() + endif() + + # check for committed changes + execute_process( + COMMAND ${GIT_EXECUTABLE} diff -s --exit-code ${gitTag} + RESULT_VARIABLE resultGitDiff + OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_QUIET + WORKING_DIRECTORY ${repoPath} + ) + + if(${resultGitDiff} EQUAL 0) + set(${isClean} + TRUE + PARENT_SCOPE + ) + else() + set(${isClean} + FALSE + PARENT_SCOPE + ) + endif() + +endfunction() + +# Add PATCH_COMMAND to CPM_ARGS_UNPARSED_ARGUMENTS. This method consumes a list of files in ARGN +# then generates a `PATCH_COMMAND` appropriate for `ExternalProject_Add()`. This command is appended +# to the parent scope's `CPM_ARGS_UNPARSED_ARGUMENTS`. +function(cpm_add_patches) + # Return if no patch files are supplied. + if(NOT ARGN) + return() + endif() + + # Find the patch program. + find_program(PATCH_EXECUTABLE patch) + if(WIN32 AND NOT PATCH_EXECUTABLE) + # The Windows git executable is distributed with patch.exe. Find the path to the executable, if + # it exists, then search `../usr/bin` and `../../usr/bin` for patch.exe. + find_package(Git QUIET) + if(GIT_EXECUTABLE) + get_filename_component(extra_search_path ${GIT_EXECUTABLE} DIRECTORY) + get_filename_component(extra_search_path_1up ${extra_search_path} DIRECTORY) + get_filename_component(extra_search_path_2up ${extra_search_path_1up} DIRECTORY) + find_program( + PATCH_EXECUTABLE patch HINTS "${extra_search_path_1up}/usr/bin" + "${extra_search_path_2up}/usr/bin" + ) + endif() + endif() + if(NOT PATCH_EXECUTABLE) + message(FATAL_ERROR "Couldn't find `patch` executable to use with PATCHES keyword.") + endif() + + # Create a temporary + set(temp_list ${CPM_ARGS_UNPARSED_ARGUMENTS}) + + # Ensure each file exists (or error out) and add it to the list. + set(first_item True) + foreach(PATCH_FILE ${ARGN}) + # Make sure the patch file exists, if we can't find it, try again in the current directory. + if(NOT EXISTS "${PATCH_FILE}") + if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/${PATCH_FILE}") + message(FATAL_ERROR "Couldn't find patch file: '${PATCH_FILE}'") + endif() + set(PATCH_FILE "${CMAKE_CURRENT_LIST_DIR}/${PATCH_FILE}") + endif() + + # Convert to absolute path for use with patch file command. + get_filename_component(PATCH_FILE "${PATCH_FILE}" ABSOLUTE) + + # The first patch entry must be preceded by "PATCH_COMMAND" while the following items are + # preceded by "&&". + if(first_item) + set(first_item False) + list(APPEND temp_list "PATCH_COMMAND") + else() + list(APPEND temp_list "&&") + endif() + # Add the patch command to the list + list(APPEND temp_list "${PATCH_EXECUTABLE}" "-p1" "<" "${PATCH_FILE}") + endforeach() + + # Move temp out into parent scope. + set(CPM_ARGS_UNPARSED_ARGUMENTS + ${temp_list} + PARENT_SCOPE + ) + +endfunction() + +# method to overwrite internal FetchContent properties, to allow using CPM.cmake to overload +# FetchContent calls. As these are internal cmake properties, this method should be used carefully +# and may need modification in future CMake versions. Source: +# https://github.com/Kitware/CMake/blob/dc3d0b5a0a7d26d43d6cfeb511e224533b5d188f/Modules/FetchContent.cmake#L1152 +function(cpm_override_fetchcontent contentName) + cmake_parse_arguments(PARSE_ARGV 1 arg "" "SOURCE_DIR;BINARY_DIR" "") + if(NOT "${arg_UNPARSED_ARGUMENTS}" STREQUAL "") + message(FATAL_ERROR "${CPM_INDENT} Unsupported arguments: ${arg_UNPARSED_ARGUMENTS}") + endif() + + string(TOLOWER ${contentName} contentNameLower) + set(prefix "_FetchContent_${contentNameLower}") + + set(propertyName "${prefix}_sourceDir") + define_property( + GLOBAL + PROPERTY ${propertyName} + BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" + FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" + ) + set_property(GLOBAL PROPERTY ${propertyName} "${arg_SOURCE_DIR}") + + set(propertyName "${prefix}_binaryDir") + define_property( + GLOBAL + PROPERTY ${propertyName} + BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" + FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" + ) + set_property(GLOBAL PROPERTY ${propertyName} "${arg_BINARY_DIR}") + + set(propertyName "${prefix}_populated") + define_property( + GLOBAL + PROPERTY ${propertyName} + BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" + FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" + ) + set_property(GLOBAL PROPERTY ${propertyName} TRUE) +endfunction() + +# Download and add a package from source +function(CPMAddPackage) + cpm_set_policies() + + list(LENGTH ARGN argnLength) + if(argnLength EQUAL 1) + cpm_parse_add_package_single_arg("${ARGN}" ARGN) + + # The shorthand syntax implies EXCLUDE_FROM_ALL and SYSTEM + set(ARGN "${ARGN};EXCLUDE_FROM_ALL;YES;SYSTEM;YES;") + endif() + + set(oneValueArgs + NAME + FORCE + VERSION + GIT_TAG + DOWNLOAD_ONLY + GITHUB_REPOSITORY + GITLAB_REPOSITORY + BITBUCKET_REPOSITORY + GIT_REPOSITORY + SOURCE_DIR + FIND_PACKAGE_ARGUMENTS + NO_CACHE + SYSTEM + GIT_SHALLOW + EXCLUDE_FROM_ALL + SOURCE_SUBDIR + CUSTOM_CACHE_KEY + ) + + set(multiValueArgs URL OPTIONS DOWNLOAD_COMMAND PATCHES) + + cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}") + + # Set default values for arguments + + if(NOT DEFINED CPM_ARGS_VERSION) + if(DEFINED CPM_ARGS_GIT_TAG) + cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION) + endif() + endif() + + if(CPM_ARGS_DOWNLOAD_ONLY) + set(DOWNLOAD_ONLY ${CPM_ARGS_DOWNLOAD_ONLY}) + else() + set(DOWNLOAD_ONLY NO) + endif() + + if(DEFINED CPM_ARGS_GITHUB_REPOSITORY) + set(CPM_ARGS_GIT_REPOSITORY "https://github.com/${CPM_ARGS_GITHUB_REPOSITORY}.git") + elseif(DEFINED CPM_ARGS_GITLAB_REPOSITORY) + set(CPM_ARGS_GIT_REPOSITORY "https://gitlab.com/${CPM_ARGS_GITLAB_REPOSITORY}.git") + elseif(DEFINED CPM_ARGS_BITBUCKET_REPOSITORY) + set(CPM_ARGS_GIT_REPOSITORY "https://bitbucket.org/${CPM_ARGS_BITBUCKET_REPOSITORY}.git") + endif() + + if(DEFINED CPM_ARGS_GIT_REPOSITORY) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_REPOSITORY ${CPM_ARGS_GIT_REPOSITORY}) + if(NOT DEFINED CPM_ARGS_GIT_TAG) + set(CPM_ARGS_GIT_TAG v${CPM_ARGS_VERSION}) + endif() + + # If a name wasn't provided, try to infer it from the git repo + if(NOT DEFINED CPM_ARGS_NAME) + cpm_package_name_from_git_uri(${CPM_ARGS_GIT_REPOSITORY} CPM_ARGS_NAME) + endif() + endif() + + set(CPM_SKIP_FETCH FALSE) + + if(DEFINED CPM_ARGS_GIT_TAG) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_TAG ${CPM_ARGS_GIT_TAG}) + # If GIT_SHALLOW is explicitly specified, honor the value. + if(DEFINED CPM_ARGS_GIT_SHALLOW) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_SHALLOW ${CPM_ARGS_GIT_SHALLOW}) + endif() + endif() + + if(DEFINED CPM_ARGS_URL) + # If a name or version aren't provided, try to infer them from the URL + list(GET CPM_ARGS_URL 0 firstUrl) + cpm_package_name_and_ver_from_url(${firstUrl} nameFromUrl verFromUrl) + # If we fail to obtain name and version from the first URL, we could try other URLs if any. + # However multiple URLs are expected to be quite rare, so for now we won't bother. + + # If the caller provided their own name and version, they trump the inferred ones. + if(NOT DEFINED CPM_ARGS_NAME) + set(CPM_ARGS_NAME ${nameFromUrl}) + endif() + if(NOT DEFINED CPM_ARGS_VERSION) + set(CPM_ARGS_VERSION ${verFromUrl}) + endif() + + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS URL "${CPM_ARGS_URL}") + endif() + + # Check for required arguments + + if(NOT DEFINED CPM_ARGS_NAME) + message( + FATAL_ERROR + "${CPM_INDENT} 'NAME' was not provided and couldn't be automatically inferred for package added with arguments: '${ARGN}'" + ) + endif() + + # Check if package has been added before + cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}") + if(CPM_PACKAGE_ALREADY_ADDED) + cpm_export_variables(${CPM_ARGS_NAME}) + return() + endif() + + # Check for manual overrides + if(NOT CPM_ARGS_FORCE AND NOT "${CPM_${CPM_ARGS_NAME}_SOURCE}" STREQUAL "") + set(PACKAGE_SOURCE ${CPM_${CPM_ARGS_NAME}_SOURCE}) + set(CPM_${CPM_ARGS_NAME}_SOURCE "") + CPMAddPackage( + NAME "${CPM_ARGS_NAME}" + SOURCE_DIR "${PACKAGE_SOURCE}" + EXCLUDE_FROM_ALL "${CPM_ARGS_EXCLUDE_FROM_ALL}" + SYSTEM "${CPM_ARGS_SYSTEM}" + PATCHES "${CPM_ARGS_PATCHES}" + OPTIONS "${CPM_ARGS_OPTIONS}" + SOURCE_SUBDIR "${CPM_ARGS_SOURCE_SUBDIR}" + DOWNLOAD_ONLY "${DOWNLOAD_ONLY}" + FORCE True + ) + cpm_export_variables(${CPM_ARGS_NAME}) + return() + endif() + + # Check for available declaration + if(NOT CPM_ARGS_FORCE AND NOT "${CPM_DECLARATION_${CPM_ARGS_NAME}}" STREQUAL "") + set(declaration ${CPM_DECLARATION_${CPM_ARGS_NAME}}) + set(CPM_DECLARATION_${CPM_ARGS_NAME} "") + CPMAddPackage(${declaration}) + cpm_export_variables(${CPM_ARGS_NAME}) + # checking again to ensure version and option compatibility + cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}") + return() + endif() + + if(NOT CPM_ARGS_FORCE) + if(CPM_USE_LOCAL_PACKAGES OR CPM_LOCAL_PACKAGES_ONLY) + cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS}) + + if(CPM_PACKAGE_FOUND) + cpm_export_variables(${CPM_ARGS_NAME}) + return() + endif() + + if(CPM_LOCAL_PACKAGES_ONLY) + message( + SEND_ERROR + "${CPM_INDENT} ${CPM_ARGS_NAME} not found via find_package(${CPM_ARGS_NAME} ${CPM_ARGS_VERSION})" + ) + endif() + endif() + endif() + + CPMRegisterPackage("${CPM_ARGS_NAME}" "${CPM_ARGS_VERSION}") + + if(DEFINED CPM_ARGS_GIT_TAG) + set(PACKAGE_INFO "${CPM_ARGS_GIT_TAG}") + elseif(DEFINED CPM_ARGS_SOURCE_DIR) + set(PACKAGE_INFO "${CPM_ARGS_SOURCE_DIR}") + else() + set(PACKAGE_INFO "${CPM_ARGS_VERSION}") + endif() + + if(DEFINED FETCHCONTENT_BASE_DIR) + # respect user's FETCHCONTENT_BASE_DIR if set + set(CPM_FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR}) + else() + set(CPM_FETCHCONTENT_BASE_DIR ${CMAKE_BINARY_DIR}/_deps) + endif() + + cpm_add_patches(${CPM_ARGS_PATCHES}) + + if(DEFINED CPM_ARGS_DOWNLOAD_COMMAND) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS DOWNLOAD_COMMAND ${CPM_ARGS_DOWNLOAD_COMMAND}) + elseif(DEFINED CPM_ARGS_SOURCE_DIR) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS SOURCE_DIR ${CPM_ARGS_SOURCE_DIR}) + if(NOT IS_ABSOLUTE ${CPM_ARGS_SOURCE_DIR}) + # Expand `CPM_ARGS_SOURCE_DIR` relative path. This is important because EXISTS doesn't work + # for relative paths. + get_filename_component( + source_directory ${CPM_ARGS_SOURCE_DIR} REALPATH BASE_DIR ${CMAKE_CURRENT_BINARY_DIR} + ) + else() + set(source_directory ${CPM_ARGS_SOURCE_DIR}) + endif() + if(NOT EXISTS ${source_directory}) + string(TOLOWER ${CPM_ARGS_NAME} lower_case_name) + # remove timestamps so CMake will re-download the dependency + file(REMOVE_RECURSE "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild") + endif() + elseif(CPM_SOURCE_CACHE AND NOT CPM_ARGS_NO_CACHE) + string(TOLOWER ${CPM_ARGS_NAME} lower_case_name) + set(origin_parameters ${CPM_ARGS_UNPARSED_ARGUMENTS}) + list(SORT origin_parameters) + if(CPM_ARGS_CUSTOM_CACHE_KEY) + # Application set a custom unique directory name + set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${CPM_ARGS_CUSTOM_CACHE_KEY}) + elseif(CPM_USE_NAMED_CACHE_DIRECTORIES) + string(SHA1 origin_hash "${origin_parameters};NEW_CACHE_STRUCTURE_TAG") + set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash}/${CPM_ARGS_NAME}) + else() + string(SHA1 origin_hash "${origin_parameters}") + set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash}) + endif() + # Expand `download_directory` relative path. This is important because EXISTS doesn't work for + # relative paths. + get_filename_component(download_directory ${download_directory} ABSOLUTE) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS SOURCE_DIR ${download_directory}) + + if(CPM_SOURCE_CACHE) + file(LOCK ${download_directory}/../cmake.lock) + endif() + + if(EXISTS ${download_directory}) + if(CPM_SOURCE_CACHE) + file(LOCK ${download_directory}/../cmake.lock RELEASE) + endif() + + cpm_store_fetch_properties( + ${CPM_ARGS_NAME} "${download_directory}" + "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-build" + ) + cpm_get_fetch_properties("${CPM_ARGS_NAME}") + + if(DEFINED CPM_ARGS_GIT_TAG AND NOT (PATCH_COMMAND IN_LIST CPM_ARGS_UNPARSED_ARGUMENTS)) + # warn if cache has been changed since checkout + cpm_check_git_working_dir_is_clean(${download_directory} ${CPM_ARGS_GIT_TAG} IS_CLEAN) + if(NOT ${IS_CLEAN}) + message( + WARNING "${CPM_INDENT} Cache for ${CPM_ARGS_NAME} (${download_directory}) is dirty" + ) + endif() + endif() + + cpm_add_subdirectory( + "${CPM_ARGS_NAME}" + "${DOWNLOAD_ONLY}" + "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}" + "${${CPM_ARGS_NAME}_BINARY_DIR}" + "${CPM_ARGS_EXCLUDE_FROM_ALL}" + "${CPM_ARGS_SYSTEM}" + "${CPM_ARGS_OPTIONS}" + ) + set(PACKAGE_INFO "${PACKAGE_INFO} at ${download_directory}") + + # As the source dir is already cached/populated, we override the call to FetchContent. + set(CPM_SKIP_FETCH TRUE) + cpm_override_fetchcontent( + "${lower_case_name}" SOURCE_DIR "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}" + BINARY_DIR "${${CPM_ARGS_NAME}_BINARY_DIR}" + ) + + else() + # Enable shallow clone when GIT_TAG is not a commit hash. Our guess may not be accurate, but + # it should guarantee no commit hash get mis-detected. + if(NOT DEFINED CPM_ARGS_GIT_SHALLOW) + cpm_is_git_tag_commit_hash("${CPM_ARGS_GIT_TAG}" IS_HASH) + if(NOT ${IS_HASH}) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_SHALLOW TRUE) + endif() + endif() + + # remove timestamps so CMake will re-download the dependency + file(REMOVE_RECURSE ${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild) + set(PACKAGE_INFO "${PACKAGE_INFO} to ${download_directory}") + endif() + endif() + + cpm_create_module_file(${CPM_ARGS_NAME} "CPMAddPackage(\"${ARGN}\")") + + if(CPM_PACKAGE_LOCK_ENABLED) + if((CPM_ARGS_VERSION AND NOT CPM_ARGS_SOURCE_DIR) OR CPM_INCLUDE_ALL_IN_PACKAGE_LOCK) + cpm_add_to_package_lock(${CPM_ARGS_NAME} "${ARGN}") + elseif(CPM_ARGS_SOURCE_DIR) + cpm_add_comment_to_package_lock(${CPM_ARGS_NAME} "local directory") + else() + cpm_add_comment_to_package_lock(${CPM_ARGS_NAME} "${ARGN}") + endif() + endif() + + cpm_message( + STATUS "${CPM_INDENT} Adding package ${CPM_ARGS_NAME}@${CPM_ARGS_VERSION} (${PACKAGE_INFO})" + ) + + if(NOT CPM_SKIP_FETCH) + # CMake 3.28 added EXCLUDE, SYSTEM (3.25), and SOURCE_SUBDIR (3.18) to FetchContent_Declare. + # Calling FetchContent_MakeAvailable will then internally forward these options to + # add_subdirectory. Up until these changes, we had to call FetchContent_Populate and + # add_subdirectory separately, which is no longer necessary and has been deprecated as of 3.30. + set(fetchContentDeclareExtraArgs "") + if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.28.0") + if(${CPM_ARGS_EXCLUDE_FROM_ALL}) + list(APPEND fetchContentDeclareExtraArgs EXCLUDE_FROM_ALL) + endif() + if(${CPM_ARGS_SYSTEM}) + list(APPEND fetchContentDeclareExtraArgs SYSTEM) + endif() + if(DEFINED CPM_ARGS_SOURCE_SUBDIR) + list(APPEND fetchContentDeclareExtraArgs SOURCE_SUBDIR ${CPM_ARGS_SOURCE_SUBDIR}) + endif() + # For CMake version <3.28 OPTIONS are parsed in cpm_add_subdirectory + if(CPM_ARGS_OPTIONS AND NOT DOWNLOAD_ONLY) + foreach(OPTION ${CPM_ARGS_OPTIONS}) + cpm_parse_option("${OPTION}") + set(${OPTION_KEY} "${OPTION_VALUE}") + endforeach() + endif() + endif() + cpm_declare_fetch( + "${CPM_ARGS_NAME}" ${fetchContentDeclareExtraArgs} "${CPM_ARGS_UNPARSED_ARGUMENTS}" + ) + + cpm_fetch_package("${CPM_ARGS_NAME}" ${DOWNLOAD_ONLY} populated ${CPM_ARGS_UNPARSED_ARGUMENTS}) + if(CPM_SOURCE_CACHE AND download_directory) + file(LOCK ${download_directory}/../cmake.lock RELEASE) + endif() + if(${populated} AND ${CMAKE_VERSION} VERSION_LESS "3.28.0") + cpm_add_subdirectory( + "${CPM_ARGS_NAME}" + "${DOWNLOAD_ONLY}" + "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}" + "${${CPM_ARGS_NAME}_BINARY_DIR}" + "${CPM_ARGS_EXCLUDE_FROM_ALL}" + "${CPM_ARGS_SYSTEM}" + "${CPM_ARGS_OPTIONS}" + ) + endif() + cpm_get_fetch_properties("${CPM_ARGS_NAME}") + endif() + + set(${CPM_ARGS_NAME}_ADDED YES) + cpm_export_variables("${CPM_ARGS_NAME}") +endfunction() + +# Fetch a previously declared package +macro(CPMGetPackage Name) + if(DEFINED "CPM_DECLARATION_${Name}") + CPMAddPackage(NAME ${Name}) + else() + message(SEND_ERROR "${CPM_INDENT} Cannot retrieve package ${Name}: no declaration available") + endif() +endmacro() + +# export variables available to the caller to the parent scope expects ${CPM_ARGS_NAME} to be set +macro(cpm_export_variables name) + set(${name}_SOURCE_DIR + "${${name}_SOURCE_DIR}" + PARENT_SCOPE + ) + set(${name}_BINARY_DIR + "${${name}_BINARY_DIR}" + PARENT_SCOPE + ) + set(${name}_ADDED + "${${name}_ADDED}" + PARENT_SCOPE + ) + set(CPM_LAST_PACKAGE_NAME + "${name}" + PARENT_SCOPE + ) +endmacro() + +# declares a package, so that any call to CPMAddPackage for the package name will use these +# arguments instead. Previous declarations will not be overridden. +macro(CPMDeclarePackage Name) + if(NOT DEFINED "CPM_DECLARATION_${Name}") + set("CPM_DECLARATION_${Name}" "${ARGN}") + endif() +endmacro() + +function(cpm_add_to_package_lock Name) + if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) + cpm_prettify_package_arguments(PRETTY_ARGN false ${ARGN}) + file(APPEND ${CPM_PACKAGE_LOCK_FILE} "# ${Name}\nCPMDeclarePackage(${Name}\n${PRETTY_ARGN})\n") + endif() +endfunction() + +function(cpm_add_comment_to_package_lock Name) + if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) + cpm_prettify_package_arguments(PRETTY_ARGN true ${ARGN}) + file(APPEND ${CPM_PACKAGE_LOCK_FILE} + "# ${Name} (unversioned)\n# CPMDeclarePackage(${Name}\n${PRETTY_ARGN}#)\n" + ) + endif() +endfunction() + +# includes the package lock file if it exists and creates a target `cpm-update-package-lock` to +# update it +macro(CPMUsePackageLock file) + if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) + get_filename_component(CPM_ABSOLUTE_PACKAGE_LOCK_PATH ${file} ABSOLUTE) + if(EXISTS ${CPM_ABSOLUTE_PACKAGE_LOCK_PATH}) + include(${CPM_ABSOLUTE_PACKAGE_LOCK_PATH}) + endif() + if(NOT TARGET cpm-update-package-lock) + add_custom_target( + cpm-update-package-lock COMMAND ${CMAKE_COMMAND} -E copy ${CPM_PACKAGE_LOCK_FILE} + ${CPM_ABSOLUTE_PACKAGE_LOCK_PATH} + ) + endif() + set(CPM_PACKAGE_LOCK_ENABLED true) + endif() +endmacro() + +# registers a package that has been added to CPM +function(CPMRegisterPackage PACKAGE VERSION) + list(APPEND CPM_PACKAGES ${PACKAGE}) + set(CPM_PACKAGES + ${CPM_PACKAGES} + CACHE INTERNAL "" + ) + set("CPM_PACKAGE_${PACKAGE}_VERSION" + ${VERSION} + CACHE INTERNAL "" + ) +endfunction() + +# retrieve the current version of the package to ${OUTPUT} +function(CPMGetPackageVersion PACKAGE OUTPUT) + set(${OUTPUT} + "${CPM_PACKAGE_${PACKAGE}_VERSION}" + PARENT_SCOPE + ) +endfunction() + +# declares a package in FetchContent_Declare +function(cpm_declare_fetch PACKAGE) + if(${CPM_DRY_RUN}) + cpm_message(STATUS "${CPM_INDENT} Package not declared (dry run)") + return() + endif() + + FetchContent_Declare(${PACKAGE} ${ARGN}) +endfunction() + +# returns properties for a package previously defined by cpm_declare_fetch +function(cpm_get_fetch_properties PACKAGE) + if(${CPM_DRY_RUN}) + return() + endif() + + set(${PACKAGE}_SOURCE_DIR + "${CPM_PACKAGE_${PACKAGE}_SOURCE_DIR}" + PARENT_SCOPE + ) + set(${PACKAGE}_BINARY_DIR + "${CPM_PACKAGE_${PACKAGE}_BINARY_DIR}" + PARENT_SCOPE + ) +endfunction() + +function(cpm_store_fetch_properties PACKAGE source_dir binary_dir) + if(${CPM_DRY_RUN}) + return() + endif() + + set(CPM_PACKAGE_${PACKAGE}_SOURCE_DIR + "${source_dir}" + CACHE INTERNAL "" + ) + set(CPM_PACKAGE_${PACKAGE}_BINARY_DIR + "${binary_dir}" + CACHE INTERNAL "" + ) +endfunction() + +# adds a package as a subdirectory if viable, according to provided options +function( + cpm_add_subdirectory + PACKAGE + DOWNLOAD_ONLY + SOURCE_DIR + BINARY_DIR + EXCLUDE + SYSTEM + OPTIONS +) + + if(NOT DOWNLOAD_ONLY AND EXISTS ${SOURCE_DIR}/CMakeLists.txt) + set(addSubdirectoryExtraArgs "") + if(EXCLUDE) + list(APPEND addSubdirectoryExtraArgs EXCLUDE_FROM_ALL) + endif() + if("${SYSTEM}" AND "${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.25") + # https://cmake.org/cmake/help/latest/prop_dir/SYSTEM.html#prop_dir:SYSTEM + list(APPEND addSubdirectoryExtraArgs SYSTEM) + endif() + if(OPTIONS) + foreach(OPTION ${OPTIONS}) + cpm_parse_option("${OPTION}") + set(${OPTION_KEY} "${OPTION_VALUE}") + endforeach() + endif() + set(CPM_OLD_INDENT "${CPM_INDENT}") + set(CPM_INDENT "${CPM_INDENT} ${PACKAGE}:") + add_subdirectory(${SOURCE_DIR} ${BINARY_DIR} ${addSubdirectoryExtraArgs}) + set(CPM_INDENT "${CPM_OLD_INDENT}") + endif() +endfunction() + +# downloads a previously declared package via FetchContent and exports the variables +# `${PACKAGE}_SOURCE_DIR` and `${PACKAGE}_BINARY_DIR` to the parent scope +function(cpm_fetch_package PACKAGE DOWNLOAD_ONLY populated) + set(${populated} + FALSE + PARENT_SCOPE + ) + if(${CPM_DRY_RUN}) + cpm_message(STATUS "${CPM_INDENT} Package ${PACKAGE} not fetched (dry run)") + return() + endif() + + FetchContent_GetProperties(${PACKAGE}) + + string(TOLOWER "${PACKAGE}" lower_case_name) + + if(NOT ${lower_case_name}_POPULATED) + if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.28.0") + if(DOWNLOAD_ONLY) + # MakeAvailable will call add_subdirectory internally which is not what we want when + # DOWNLOAD_ONLY is set. Populate will only download the dependency without adding it to the + # build + FetchContent_Populate( + ${PACKAGE} + SOURCE_DIR "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-src" + BINARY_DIR "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-build" + SUBBUILD_DIR "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild" + ${ARGN} + ) + else() + FetchContent_MakeAvailable(${PACKAGE}) + endif() + else() + FetchContent_Populate(${PACKAGE}) + endif() + set(${populated} + TRUE + PARENT_SCOPE + ) + endif() + + cpm_store_fetch_properties( + ${CPM_ARGS_NAME} ${${lower_case_name}_SOURCE_DIR} ${${lower_case_name}_BINARY_DIR} + ) + + set(${PACKAGE}_SOURCE_DIR + ${${lower_case_name}_SOURCE_DIR} + PARENT_SCOPE + ) + set(${PACKAGE}_BINARY_DIR + ${${lower_case_name}_BINARY_DIR} + PARENT_SCOPE + ) +endfunction() + +# splits a package option +function(cpm_parse_option OPTION) + string(REGEX MATCH "^[^ ]+" OPTION_KEY "${OPTION}") + string(LENGTH "${OPTION}" OPTION_LENGTH) + string(LENGTH "${OPTION_KEY}" OPTION_KEY_LENGTH) + if(OPTION_KEY_LENGTH STREQUAL OPTION_LENGTH) + # no value for key provided, assume user wants to set option to "ON" + set(OPTION_VALUE "ON") + else() + math(EXPR OPTION_KEY_LENGTH "${OPTION_KEY_LENGTH}+1") + string(SUBSTRING "${OPTION}" "${OPTION_KEY_LENGTH}" "-1" OPTION_VALUE) + endif() + set(OPTION_KEY + "${OPTION_KEY}" + PARENT_SCOPE + ) + set(OPTION_VALUE + "${OPTION_VALUE}" + PARENT_SCOPE + ) +endfunction() + +# guesses the package version from a git tag +function(cpm_get_version_from_git_tag GIT_TAG RESULT) + string(LENGTH ${GIT_TAG} length) + if(length EQUAL 40) + # GIT_TAG is probably a git hash + set(${RESULT} + 0 + PARENT_SCOPE + ) + else() + string(REGEX MATCH "v?([0123456789.]*).*" _ ${GIT_TAG}) + set(${RESULT} + ${CMAKE_MATCH_1} + PARENT_SCOPE + ) + endif() +endfunction() + +# guesses if the git tag is a commit hash or an actual tag or a branch name. +function(cpm_is_git_tag_commit_hash GIT_TAG RESULT) + string(LENGTH "${GIT_TAG}" length) + # full hash has 40 characters, and short hash has at least 7 characters. + if(length LESS 7 OR length GREATER 40) + set(${RESULT} + 0 + PARENT_SCOPE + ) + else() + if(${GIT_TAG} MATCHES "^[a-fA-F0-9]+$") + set(${RESULT} + 1 + PARENT_SCOPE + ) + else() + set(${RESULT} + 0 + PARENT_SCOPE + ) + endif() + endif() +endfunction() + +function(cpm_prettify_package_arguments OUT_VAR IS_IN_COMMENT) + set(oneValueArgs + NAME + FORCE + VERSION + GIT_TAG + DOWNLOAD_ONLY + GITHUB_REPOSITORY + GITLAB_REPOSITORY + BITBUCKET_REPOSITORY + GIT_REPOSITORY + SOURCE_DIR + FIND_PACKAGE_ARGUMENTS + NO_CACHE + SYSTEM + GIT_SHALLOW + EXCLUDE_FROM_ALL + SOURCE_SUBDIR + ) + set(multiValueArgs URL OPTIONS DOWNLOAD_COMMAND) + cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + foreach(oneArgName ${oneValueArgs}) + if(DEFINED CPM_ARGS_${oneArgName}) + if(${IS_IN_COMMENT}) + string(APPEND PRETTY_OUT_VAR "#") + endif() + if(${oneArgName} STREQUAL "SOURCE_DIR") + string(REPLACE ${CMAKE_SOURCE_DIR} "\${CMAKE_SOURCE_DIR}" CPM_ARGS_${oneArgName} + ${CPM_ARGS_${oneArgName}} + ) + endif() + string(APPEND PRETTY_OUT_VAR " ${oneArgName} ${CPM_ARGS_${oneArgName}}\n") + endif() + endforeach() + foreach(multiArgName ${multiValueArgs}) + if(DEFINED CPM_ARGS_${multiArgName}) + if(${IS_IN_COMMENT}) + string(APPEND PRETTY_OUT_VAR "#") + endif() + string(APPEND PRETTY_OUT_VAR " ${multiArgName}\n") + foreach(singleOption ${CPM_ARGS_${multiArgName}}) + if(${IS_IN_COMMENT}) + string(APPEND PRETTY_OUT_VAR "#") + endif() + string(APPEND PRETTY_OUT_VAR " \"${singleOption}\"\n") + endforeach() + endif() + endforeach() + + if(NOT "${CPM_ARGS_UNPARSED_ARGUMENTS}" STREQUAL "") + if(${IS_IN_COMMENT}) + string(APPEND PRETTY_OUT_VAR "#") + endif() + string(APPEND PRETTY_OUT_VAR " ") + foreach(CPM_ARGS_UNPARSED_ARGUMENT ${CPM_ARGS_UNPARSED_ARGUMENTS}) + string(APPEND PRETTY_OUT_VAR " ${CPM_ARGS_UNPARSED_ARGUMENT}") + endforeach() + string(APPEND PRETTY_OUT_VAR "\n") + endif() + + set(${OUT_VAR} + ${PRETTY_OUT_VAR} + PARENT_SCOPE + ) + +endfunction() diff --git a/cmake/FindMySQL.cmake b/cmake/FindMySQL.cmake deleted file mode 100644 index c482816..0000000 --- a/cmake/FindMySQL.cmake +++ /dev/null @@ -1,129 +0,0 @@ -# - Find mysqlclient -# Find the native MySQL includes and library -# -# MYSQL_INCLUDE_DIR - where to find mysql.h, etc. -# MYSQL_LIBRARY - List of libraries when using MySQL. -# MYSQL_FOUND - True if MySQL found. - -IF (MYSQL_INCLUDE_DIR) - # Already in cache, be silent - SET(MYSQL_FIND_QUIETLY TRUE) -ENDIF (MYSQL_INCLUDE_DIR) - -if(WIN32) - find_path(MYSQL_INCLUDE_DIR mysql.h - PATHS - $ENV{MYSQL_INCLUDE_DIR} - $ENV{MYSQL_DIR}/include - $ENV{ProgramFiles}/MySQL/*/include - $ENV{SystemDrive}/MySQL/*/include - $ENV{ProgramW6432}/MySQL/*/include - $ENV{ProgramFiles}/MariaDB/*/include - $ENV{SystemDrive}/MariaDB/*/include - $ENV{ProgramW6432}/MariaDB/*/include - ) -else(WIN32) - find_path(MYSQL_INCLUDE_DIR mysql.h - PATHS - $ENV{MYSQL_DIR}/include - $ENV{MYSQL_INCLUDE_DIR} - /usr/local/mysql/include - /opt/mysql/mysql/include - PATH_SUFFIXES - mysql - ) -endif(WIN32) - -if(WIN32) - if (${CMAKE_BUILD_TYPE}) - STRING(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_TOLOWER) - endif() - - # path suffix for debug/release mode - # binary_dist: mysql binary distribution - # build_dist: custom build - if(CMAKE_BUILD_TYPE_TOLOWER MATCHES "debug") - SET(binary_dist debug) - SET(build_dist Debug) - else(CMAKE_BUILD_TYPE_TOLOWER MATCHES "debug") - ADD_DEFINITIONS(-DDBUG_OFF) - SET(binary_dist opt) - SET(build_dist Release) - endif(CMAKE_BUILD_TYPE_TOLOWER MATCHES "debug") - - FIND_LIBRARY(MYSQL_LIBRARY NAMES libmysql libmariadb - PATHS - $ENV{MYSQL_DIR}/lib - # $ENV{MYSQL_DIR}/lib/${binary_dist} - # $ENV{MYSQL_DIR}/libmysql/${build_dist} - # $ENV{MYSQL_DIR}/client/${build_dist} - # $ENV{ProgramFiles}/MySQL/*/lib/${binary_dist} - $ENV{ProgramFiles}/MySQL/*/lib - $ENV{SystemDrive}/MySQL/*/lib - $ENV{ProgramW6432}/MySQL/*/lib - $ENV{ProgramFiles}/MariaDB/*/lib - $ENV{SystemDrive}/MariaDB/*/lib - $ENV{ProgramW6432}/MariaDB/*/lib - # $ENV{SystemDrive}/MySQL/*/lib/${binary_dist} - # $ENV{ProgramFiles}/MariaDB/*/lib/${binary_dist} - # $ENV{SystemDrive}/MariaDB/*/lib/${binary_dist} - # $ENV{MYSQL_DIR}/lib/opt - # $ENV{MYSQL_DIR}/client/release - # $ENV{ProgramFiles}/MySQL/*/lib/opt - # $ENV{SystemDrive}/MySQL/*/lib/opt - # $ENV{ProgramW6432}/MySQL/*/lib - # $ENV{ProgramFiles}/MariaDB/*/lib/opt - # $ENV{SystemDrive}/MariaDB/*/lib/opt - # $ENV{ProgramW6432}/MariaDB/*/lib - ) -else(WIN32) - find_library(MYSQL_LIBRARY NAMES libmysql - PATHS - $ENV{MYSQL_DIR}/libmysql_r/.libs - $ENV{MYSQL_DIR}/lib - $ENV{MYSQL_DIR}/lib/mysql - /usr/local/mysql/lib - /opt/mysql/mysql/lib - PATH_SUFFIXES - mysql - ) -endif(WIN32) - -if(WIN32) -else(WIN32) - set(MYSQL_LIB_PATHS - $ENV{MYSQL_DIR}/libmysql_r/.libs - $ENV{MYSQL_DIR}/lib - $ENV{MYSQL_DIR}/lib/mysql - /usr/local/mysql/lib - /opt/mysql/mysql/lib - PATH_SUFFIXES - mysql - ) - find_library(MYSQL_LIBRARY NAMES mysqlclient - PATHS - ${MYSQL_LIB_PATHS} - ) -endif(WIN32) - -IF (MYSQL_INCLUDE_DIR) - MESSAGE(STATUS "MariaDB include ${MYSQL_INCLUDE_DIR}") -ELSE (MYSQL_INCLUDE_DIR) - MESSAGE(STATUS "MariaDB include dir not found") -ENDIF (MYSQL_INCLUDE_DIR) - -IF (MYSQL_LIBRARY) - MESSAGE(STATUS "MariaDB libs ${MYSQL_LIBRARY}") -ELSE (MYSQL_LIBRARY) - MESSAGE(STATUS "MariaDB libs dir not found") -ENDIF (MYSQL_LIBRARY) - - -IF (MYSQL_INCLUDE_DIR AND MYSQL_LIBRARY) - SET(MYSQL_FOUND TRUE) -ELSE (MYSQL_INCLUDE_DIR AND MYSQL_LIBRARY) - SET(MYSQL_FOUND FALSE) - SET(MYSQL_LIBRARY) -ENDIF (MYSQL_INCLUDE_DIR AND MYSQL_LIBRARY) - -MARK_AS_ADVANCED(MYSQL_LIBRARY MYSQL_INCLUDE_DIR) diff --git a/include/matador/object/basic_object_info.hpp b/include/matador/object/basic_object_info.hpp new file mode 100644 index 0000000..b057488 --- /dev/null +++ b/include/matador/object/basic_object_info.hpp @@ -0,0 +1,40 @@ +#ifndef BASIC_PROTOTYPE_INFO_HPP +#define BASIC_PROTOTYPE_INFO_HPP + +#include + +namespace matador::object { + +class schema_node; + +class basic_object_info { +public: + virtual ~basic_object_info() = default; + + [[nodiscard]] std::type_index type_index() const; + +protected: + basic_object_info(schema_node &node, std::type_index type_index); + +protected: + schema_node &node_; /**< prototype node of the represented object type */ + std::type_index type_index_; /**< type index of the represented object type */ +}; + +template +class object_info final : public basic_object_info { +public: + explicit object_info(schema_node &node) + : basic_object_info(node, typeid(Type)) {} + +}; + +namespace detail { +struct null_type {}; +} + +using null_info = object_info; + +} + +#endif //BASIC_PROTOTYPE_INFO_HPP diff --git a/include/matador/object/error_code.hpp b/include/matador/object/error_code.hpp new file mode 100644 index 0000000..fa52219 --- /dev/null +++ b/include/matador/object/error_code.hpp @@ -0,0 +1,32 @@ +#ifndef OBJECT_ERROR_CODE_HPP +#define OBJECT_ERROR_CODE_HPP + +#include +#include + +namespace matador::object { + +enum class error_code : uint8_t { + OK = 0, + NodeNotFound = 1, + NodeAlreadyExists = 2, + Failure, +}; + +class object_category_impl final : public std::error_category +{ +public: + [[nodiscard]] const char* name() const noexcept override; + [[nodiscard]] std::string message(int ev) const override; +}; + +const std::error_category& object_category(); +std::error_code make_error_code(error_code e); +std::error_condition make_error_condition(error_code e); + +} + +template <> +struct std::is_error_code_enum : true_type {}; + +#endif //OBJECT_ERROR_CODE_HPP diff --git a/include/matador/object/schema.hpp b/include/matador/object/schema.hpp new file mode 100644 index 0000000..932bcc9 --- /dev/null +++ b/include/matador/object/schema.hpp @@ -0,0 +1,63 @@ +#ifndef SCHEMA_HPP +#define SCHEMA_HPP + +#include "matador/object/error_code.hpp" +#include "matador/object/schema_node.hpp" + +#include "matador/utils/result.hpp" +#include "matador/utils/error.hpp" + +#include +#include +#include + +namespace matador::object { + +class schema { +public: + schema(); + + template + utils::result attach(const std::string name, const std::string &parent = "") { + auto node = schema_node::make_node(*this, name); + + attach_node(node, parent); + return utils::ok(); + } + + template + utils::result attach(const std::string name) { + // auto node = std::make_unique(*this); + + return utils::ok(); + } + + [[nodiscard]] bool empty() const; + + [[nodiscard]] size_t size() const; + +private: + using t_node_map = std::unordered_map>; + // type_index -> [name -> prototype] + using t_type_index_node_map = std::unordered_map; + using node_ptr = std::shared_ptr; + + utils::result, utils::error> attach_node(const std::shared_ptr &node, + const std::string &parent); + utils::result, utils::error> find_parent(const std::string &name) const; + utils::result, utils::error> find_node(const std::string &name) const; + + utils::result, utils::error> push_back_child(const node_ptr &parent, const node_ptr &child); + + bool has_node(const std::type_index& index, const std::string &name) const; + +private: + std::shared_ptr root_; + + t_node_map node_map_; + t_type_index_node_map type_index_node_map_; +}; + +} + +#endif //SCHEMA_HPP diff --git a/include/matador/object/schema_node.hpp b/include/matador/object/schema_node.hpp new file mode 100644 index 0000000..36aed05 --- /dev/null +++ b/include/matador/object/schema_node.hpp @@ -0,0 +1,75 @@ +#ifndef SCHEMA_NODE_HPP +#define SCHEMA_NODE_HPP + +#include "matador/object/basic_object_info.hpp" + +#include +#include + +namespace matador::object { + +class schema; + +class schema_node final { +public: + template < typename Type > + static std::shared_ptr make_node(schema& tree, const std::string& name) { + return std::make_shared(tree, name, static_cast(nullptr)); + } + + schema_node(const schema_node& other) = delete; + schema_node(schema_node&& other) = default; + schema_node& operator=(const schema_node& other) = delete; + schema_node& operator=(schema_node&& other) = delete; + ~schema_node() = default; + + [[nodiscard]] std::string name() const; + [[nodiscard]] std::type_index type_index() const; + + /** + * Appends the given prototype node as a sibling + * on the same level. + * + * @param sibling The new sibling node. + */ + void append(const std::shared_ptr &sibling); + + /** + * Inserts the given node to the list of children. + * + * @param child The child node to add. + */ + void insert(const std::shared_ptr &child); + +private: + explicit schema_node(schema& tree); + template < typename Type > + schema_node(schema& tree, std::string name, Type *obj) + : schema_(tree) + , info_(std::make_unique>(*this)) + , first_child_(std::make_shared(tree)) + , last_child_(std::make_shared(tree)) + , name_(std::move(name)) { + first_child_->next_sibling_ = last_child_; + last_child_->previous_sibling_ = first_child_; + } + + +private: + friend schema; + + schema &schema_; + std::unique_ptr info_; + + std::shared_ptr parent_; + std::shared_ptr previous_sibling_; + std::shared_ptr next_sibling_; + std::shared_ptr first_child_; + std::shared_ptr last_child_; + + std::string name_; + size_t depth_{0}; +}; + +} +#endif //SCHEMA_NODE_HPP diff --git a/include/matador/query/attribute_string_writer.hpp b/include/matador/query/attribute_string_writer.hpp new file mode 100644 index 0000000..4ce909f --- /dev/null +++ b/include/matador/query/attribute_string_writer.hpp @@ -0,0 +1,63 @@ +#ifndef ATTRIBUTE_STRING_WRITER_HPP +#define ATTRIBUTE_STRING_WRITER_HPP + +#include "matador/utils/attribute_writer.hpp" + +#include + +namespace matador::sql { +class dialect; +class connection_impl; +} + +namespace matador::query { + +class attribute_string_writer final : public utils::attribute_writer +{ +public: + attribute_string_writer(const sql::dialect &d, std::optional> conn); + + template + [[nodiscard]] std::string to_string(const Type &value) + { + result_.clear(); + write_value(0, value); + + return result_; + } + [[nodiscard]] const sql::dialect& dialect() const; + + void write_value(size_t pos, const int8_t& x) override; + void write_value(size_t pos, const int16_t& x) override; + void write_value(size_t pos, const int32_t& x) override; + void write_value(size_t pos, const int64_t& x) override; + void write_value(size_t pos, const uint8_t& x) override; + void write_value(size_t pos, const uint16_t& x) override; + void write_value(size_t pos, const uint32_t& x) override; + void write_value(size_t pos, const uint64_t& x) override; + void write_value(size_t pos, const bool& x) override; + void write_value(size_t pos, const float& x) override; + void write_value(size_t pos, const double& x) override; + void write_value(size_t pos, const time& x) override; + void write_value(size_t pos, const date& x) override; + void write_value(size_t pos, const char* x) override; + void write_value(size_t pos, const char* x, size_t size) override; + void write_value(size_t pos, const std::string& x) override; + void write_value(size_t pos, const std::string& x, size_t size) override; + void write_value(size_t pos, const utils::blob& x) override; + void write_value(size_t pos, const utils::value& x, size_t size) override; + +private: + std::string result_; + const sql::dialect &dialect_; + std::optional> conn_; +}; + +// "This is a binary Data string" as binary data: +// MySQL: X'5468697320697320612062616E617279204461746120737472696E67' +// Postgres: E'\\x5468697320697320612062616E617279204461746120737472696E67' +// MSSQL: 0x5468697320697320612062616E617279204461746120737472696E67 +// Sqlite: X'5468697320697320612062616E617279204461746120737472696E67' + +} +#endif //ATTRIBUTE_STRING_WRITER_HPP diff --git a/include/matador/sql/basic_condition.hpp b/include/matador/query/basic_condition.hpp similarity index 64% rename from include/matador/sql/basic_condition.hpp rename to include/matador/query/basic_condition.hpp index daa250f..65b0456 100644 --- a/include/matador/sql/basic_condition.hpp +++ b/include/matador/query/basic_condition.hpp @@ -8,9 +8,11 @@ #include namespace matador::sql { - class dialect; -class query_context; +struct query_context; +} + +namespace matador::query { class basic_condition { @@ -18,7 +20,7 @@ public: basic_condition() = default; virtual ~basic_condition() = default; - enum class operand_t : uint8_t + enum class operand_type : uint8_t { EQUAL = 0, NOT_EQUAL, @@ -33,26 +35,26 @@ public: LIKE }; - virtual std::string evaluate(const dialect &dialect, query_context &query) const = 0; + virtual std::string evaluate(const sql::dialect &dialect, sql::query_context &query) const = 0; - static std::unordered_map operands; + static std::unordered_map operands; }; class basic_column_condition : public basic_condition { public: - column field_; + sql::column field_; std::string operand; - basic_column_condition(column fld, basic_condition::operand_t op); + basic_column_condition(sql::column fld, operand_type op); }; class basic_in_condition : public basic_condition { public: - column field_; + sql::column field_; - explicit basic_in_condition(column fld); + explicit basic_in_condition(sql::column fld); [[nodiscard]] virtual size_t size() const = 0; }; diff --git a/include/matador/sql/condition.hpp b/include/matador/query/condition.hpp similarity index 67% rename from include/matador/sql/condition.hpp rename to include/matador/query/condition.hpp index e39e439..2454525 100644 --- a/include/matador/sql/condition.hpp +++ b/include/matador/query/condition.hpp @@ -1,17 +1,17 @@ #ifndef QUERY_CONDITION_HPP #define QUERY_CONDITION_HPP -#include "matador/sql/any_type_to_string_visitor.hpp" -#include "matador/sql/query_result.hpp" -#include "matador/sql/basic_condition.hpp" +#include "matador/query/basic_condition.hpp" + #include "matador/sql/dialect.hpp" -#include "matador/sql/placeholder.hpp" #include "matador/sql/query_context.hpp" +#include "matador/utils/placeholder.hpp" + #include #include -namespace matador::sql { +namespace matador::query { /** * @class condition @@ -28,58 +28,55 @@ namespace matador::sql { /// @cond MATADOR_DEV -class query_select; - template class condition; template<> -class condition::type> : public basic_column_condition +class condition> final : public basic_column_condition { public: - condition(const column &fld, basic_condition::operand_t op, const placeholder &val); + condition(const sql::column &fld, operand_type op, const utils::placeholder &val); - placeholder value; + utils::placeholder value; - std::string evaluate(const dialect &d, query_context &query) const override; + std::string evaluate(const sql::dialect &d, sql::query_context &query) const override; }; template -class condition::value && - !std::is_enum::value && - !std::is_same::value && - !std::is_same::value>::type> : public basic_column_condition +class condition && + !std::is_same_v && + !std::is_same_v>> final : public basic_column_condition { public: - condition(const column &fld, basic_condition::operand_t op, T val) + condition(const sql::column &fld, const operand_type op, T val) : basic_column_condition(fld, op) , value(val) { } T value; - std::string evaluate(const dialect &d, query_context &query) const override + std::string evaluate(const sql::dialect &d, sql::query_context &query) const override { query.bind_vars.emplace_back(field_.name); - return d.prepare_identifier(field_) + " " + operand + " " + std::to_string(value); + return d.prepare_condition(field_) + " " + operand + " " + std::to_string(value); } }; template -class condition::value || - std::is_same::value>::type> : public basic_column_condition +class condition || + std::is_same_v>>final : public basic_column_condition { public: - condition(const column &fld, basic_condition::operand_t op, T val) + condition(const sql::column &fld, const operand_type op, T val) : basic_column_condition(fld, op) ,value(val) { } T value; - std::string evaluate(const dialect &d, query_context &query) const override + std::string evaluate(const sql::dialect &d, sql::query_context &query) const override { query.bind_vars.emplace_back(field_.name); return d.prepare_identifier(field_) + " " + operand + " '" + value + "'"; @@ -87,61 +84,39 @@ public: }; template -class condition>> final : public basic_column_condition -{ -public: - condition(const column &fld, basic_condition::operand_t op, T val) - : basic_column_condition(fld, op) - , value(val) - { } - - T value; - - std::string evaluate(const dialect &d, query_context &query) const override - { - auto at = data_type_traits::create_value(value); - any_type_to_string_visitor value_to_string(d, query); - std::visit(value_to_string, at); - return value_to_string.result + " " + operand + " " + d.prepare_identifier(field_); - } - -}; - - -template -class condition && !std::is_same_v && !std::is_same_v>> final : public basic_column_condition { public: - condition(T val, basic_condition::operand_t op, const column &fld) + condition(T val, const operand_type op, const sql::column &fld) : basic_column_condition(fld, op) , value(val) { } T value; - std::string evaluate(const dialect &d, query_context &query) const override + std::string evaluate(const sql::dialect &d, sql::query_context &query) const override { return std::to_string(value) + " " + operand + " " + d.prepare_identifier(field_); } }; template -class condition::value || - std::is_same::value>::type> : public basic_column_condition +class condition || + std::is_same_v>> final : public basic_column_condition { public: - condition(T val, basic_condition::operand_t op, const column &fld) + condition(T val, const operand_type op, const sql::column &fld) : basic_column_condition(fld, op) , value(val) { } T value; - std::string evaluate(const dialect &d, query_context &query) const override + std::string evaluate(const sql::dialect &d, sql::query_context &query) const override { return "'" + std::to_string(value) + "' " + operand + " " + d.prepare_identifier(field_); } @@ -160,18 +135,18 @@ public: * @endcode */ template < class V > -class condition> : public basic_in_condition { +class condition> final : public basic_in_condition { public: /** * @brief Creates an IN condition * - * Creates an IN condition for the given column and + * Creates an IN condition for the given sql::column and * the given list of arguments. * * @param col Column for the IN condition * @param args List of arguments */ - condition(const column &col, const std::initializer_list &args) + condition(const sql::column &col, const std::initializer_list &args) : basic_in_condition(col), args_(args) {} /** @@ -181,9 +156,10 @@ public: * query string based on the given compile type * * @param d The d used to evaluate + * @param query Query to evaluate * @return A condition IN part of the query */ - std::string evaluate(const dialect &d, query_context &query) const override { + std::string evaluate(const sql::dialect &d, sql::query_context &query) const override { auto count = size(); for (size_t i = 0; i < count; ++i) { query.bind_vars.emplace_back(field_.name); @@ -220,15 +196,15 @@ private: /** * @brief Condition class representing an IN condition * - * This class represents an query IN condition and evaluates to + * This class represents a query IN condition and evaluates to * this condition based on the current database d * * @code - * WHERE age IN (select age_value from ) + * WHERE age IN (select age_value from table) * @endcode */ template <> -class condition : public basic_column_condition +class condition final : public basic_column_condition { public: /** @@ -242,7 +218,7 @@ public: * @param op Operand of the condition * @param q The query to be evaluated to the IN arguments */ - condition(column col, basic_condition::operand_t op, const query_context &q); + condition(sql::column col, operand_type op, sql::query_context &q); /** * @brief Evaluates the condition @@ -251,12 +227,13 @@ public: * query string based on the given compile type * * @param d The d used to evaluate + * @param query Query to evaluate * @return A condition IN part of the query */ - std::string evaluate(const dialect &d, query_context &query) const override; + std::string evaluate(const sql::dialect &d, sql::query_context &query) const override; private: - query_context query_; + sql::query_context &query_; }; /** @@ -268,7 +245,7 @@ private: * @tparam T The type of the boundary values */ template < class T > -class condition> : public basic_condition { +class condition> final : public basic_condition { public: /** * @brief Create a new between condition @@ -276,7 +253,7 @@ public: * @param col The column for the range check * @param range The boundary values defining the range */ - condition(column col, const std::pair &range) + condition(sql::column col, const std::pair &range) : field_(std::move(col)), range_(range) {} /** @@ -286,16 +263,17 @@ public: * based on the given compile type * * @param d The d used to evaluate + * @param query Query to evaluate * @return A condition BETWEEN part of the query */ - std::string evaluate(const dialect &d, query_context &query) const override { + std::string evaluate(const sql::dialect &d, sql::query_context &query) const override { query.bind_vars.emplace_back(field_.name); query.bind_vars.emplace_back(field_.name); return d.prepare_identifier(field_) + " BETWEEN " + std::to_string(range_.first) + " AND " + std::to_string(range_.second); } private: - column field_; + sql::column field_; std::pair range_; }; @@ -312,7 +290,7 @@ private: * @tparam R2 The right hand type of the right operator */ template -class condition, condition> : public basic_condition +class condition, condition> final : public basic_condition { public: /** @@ -321,31 +299,32 @@ public: * @param r right hand operator of the condition * @param op The operand (AND or OR) */ - condition(condition &&l, condition &&r, basic_condition::operand_t op) + condition(condition &&l, condition &&r, const operand_type op) : left(std::move(l)), right(std::move(r)), operand(op) { } /** * @brief Evaluates the condition * * @param d The d used to evaluate + * @param query Query to evaluate * @return The evaluated string based on the compile type */ - std::string evaluate(const dialect &d, query_context &query) const override + std::string evaluate(const sql::dialect &d, sql::query_context &query) const override { // ensure the numbering order for host vars auto cl = left.evaluate(d, query); auto cr = right.evaluate(d, query); - if (operand == basic_condition::operand_t::AND) { - return "(" + cl + " " + basic_condition::operands[operand] + " " + cr + ")"; + if (operand == operand_type::AND) { + return "(" + cl + " " + operands[operand] + " " + cr + ")"; } else { - return cl + " " + basic_condition::operands[operand] + " " + cr; + return cl + " " + operands[operand] + " " + cr; } } private: condition left; condition right; - basic_condition::operand_t operand; + basic_condition::operand_type operand; }; /** @@ -357,7 +336,7 @@ private: * @tparam R Right hand type of the condition to be negated */ template -class condition, void> : public basic_condition +class condition, void> final : public basic_condition { public: /** @@ -365,17 +344,18 @@ public: * @param c The condition to be negated */ condition(const condition &c) // NOLINT(*-explicit-constructor) - : cond(c), operand(basic_condition::operands[basic_condition::operand_t::NOT]) { } + : cond(c), operand(operands[operand_type::NOT]) { } /** * @brief Evaluates the condition * * @param d The d used to evaluate + * @param query The context of the query * @return The evaluated string based on the compile type */ - std::string evaluate(const dialect &d, query_context &query) const override + std::string evaluate(const sql::dialect &d, sql::query_context &query) const override { - return operand + " (" + cond.evaluate(d) + ")"; + return operand + " (" + cond.evaluate(d, query) + ")"; } private: @@ -384,25 +364,23 @@ private: }; template<> -class condition : public basic_column_condition +class condition final : public basic_column_condition { public: - condition(const column &a, basic_condition::operand_t op, column b) + condition(const sql::column &a, const operand_type op, sql::column b) : basic_column_condition(a, op) , other_column_(std::move(b)) {} /** * @brief Evaluates the condition * * @param d The d used to evaluate + * @param query The context of the query * @return The evaluated string based on the compile type */ - std::string evaluate(const dialect &d, query_context &query) const override - { - return d.prepare_identifier(field_) + " " + operand + " " + d.prepare_identifier(other_column_); - } + std::string evaluate(const sql::dialect &d, sql::query_context &query) const override; private: - column other_column_; + sql::column other_column_; }; /** @@ -430,9 +408,9 @@ private: * @return The condition object */ template < class V > -condition> in(const column &col, std::initializer_list args) +condition> in(const sql::column &col, std::initializer_list args) { - return condition>(col, args); + return condition>(col, args); } /** @@ -442,8 +420,7 @@ condition> in(const column &col, std::initializ * @param q The query to be executes as sub select * @return The condition object */ -condition in(const column &col, const query_context &q); -condition in(const column &col, const query_select &q); +condition in(const sql::column &col, sql::query_context &&q); /** * @brief Creates a between condition. @@ -458,9 +435,9 @@ condition in(const column &col, const query_select &q); * @return The condition object */ template -condition> between(const column &col, T low, T high) +condition> between(const sql::column &col, T low, T high) { - return condition>(col, std::make_pair(low, high)); + return condition>(col, std::make_pair(low, high)); } /** @@ -473,7 +450,7 @@ condition> between(const column &col, T low, T high) * @param val The value to the like operator * @return The like condition object */ -condition like(const column &col, const std::string &val); +condition like(const sql::column &col, const std::string &val); /** * @brief Condition equality operator for a column and a value @@ -487,12 +464,12 @@ condition like(const column &col, const std::string &val); * @return The condition object representing the equality operation */ template -condition operator==(const column &col, T val) +condition operator==(const sql::column &col, T val) { - return condition(col, basic_condition::operand_t::EQUAL, val); + return condition(col, basic_condition::operand_type::EQUAL, val); } -condition operator==(const column &a, const column &b); +condition operator==(const sql::column &a, const sql::column &b); /** * @brief Condition equality method for a column and a query @@ -504,12 +481,12 @@ condition operator==(const column &a, const column &b); * @param q The query to compare with * @return The condition object representing the equality operation */ -condition equals(const column &col, query_context &q); +condition equals(const sql::column &col, sql::query_context &q); /** * @brief Condition inequality operator for a column and a value * - * Creates a condition condition object of a column and a value + * Creates a condition object of a column and a value * checked on inequality. * * @tparam T The type of the value @@ -518,9 +495,9 @@ condition equals(const column &col, query_context &q); * @return The condition object representing the inequality operation */ template -condition operator!=(const column &col, T val) +condition operator!=(const sql::column &col, T val) { - return condition(col, basic_condition::operand_t::NOT_EQUAL, val); + return condition(col, basic_condition::operand_type::NOT_EQUAL, val); } /** @@ -535,9 +512,9 @@ condition operator!=(const column &col, T val) * @return The condition object representing the less operation */ template -condition operator<(const column &col, T val) +condition operator<(const sql::column &col, T val) { - return condition(col, basic_condition::operand_t::LESS, val); + return condition(col, basic_condition::operand_type::LESS, val); } /** @@ -552,9 +529,9 @@ condition operator<(const column &col, T val) * @return The condition object representing the less or equal operation */ template -condition operator<=(const column &col, T val) +condition operator<=(const sql::column &col, T val) { - return condition(col, basic_condition::operand_t::LESS_EQUAL, val); + return condition(col, basic_condition::operand_type::LESS_EQUAL, val); } /** @@ -569,9 +546,9 @@ condition operator<=(const column &col, T val) * @return The condition object representing the greater operation */ template -condition operator>(const column &col, T val) +condition operator>(const sql::column &col, T val) { - return condition(col, basic_condition::operand_t::GREATER, val); + return condition(col, basic_condition::operand_type::GREATER, val); } /** @@ -586,9 +563,9 @@ condition operator>(const column &col, T val) * @return The condition object representing the greater or equal operation */ template -condition operator>=(const column &col, T val) +condition operator>=(const sql::column &col, T val) { - return condition(col, basic_condition::operand_t::GREATER_EQUAL, val); + return condition(col, basic_condition::operand_type::GREATER_EQUAL, val); } /** @@ -605,7 +582,7 @@ condition operator>=(const column &col, T val) template condition, condition> operator&&(condition l, condition r) { - return condition, condition>(std::move(l), std::move(r), basic_condition::operand_t::AND); + return condition, condition>(std::move(l), std::move(r), basic_condition::operand_type::AND); } /** @@ -622,7 +599,7 @@ condition, condition> operator&&(condition l, template condition, condition> operator||(condition l, condition r) { - return condition, condition>(std::move(l), std::move(r), basic_condition::operand_t::OR); + return condition, condition>(std::move(l), std::move(r), basic_condition::operand_type::OR); } /** diff --git a/include/matador/sql/fk_value_extractor.hpp b/include/matador/query/fk_value_extractor.hpp similarity index 59% rename from include/matador/sql/fk_value_extractor.hpp rename to include/matador/query/fk_value_extractor.hpp index 9184594..bc9fe39 100644 --- a/include/matador/sql/fk_value_extractor.hpp +++ b/include/matador/query/fk_value_extractor.hpp @@ -1,12 +1,12 @@ #ifndef QUERY_FK_VALUE_EXTRACTOR_HPP #define QUERY_FK_VALUE_EXTRACTOR_HPP -#include "matador/sql/any_type.hpp" - #include "matador/utils/access.hpp" #include "matador/utils/field_attributes.hpp" +#include "matador/utils/foreign_attributes.hpp" +#include "matador/utils/types.hpp" -namespace matador::sql::detail { +namespace matador::query::detail { class fk_value_extractor { @@ -14,14 +14,14 @@ public: fk_value_extractor() = default; template - any_type extract(Type &x) + utils::database_type extract(Type &x) { - matador::utils::access::process(*this, x); + access::process(*this, x); return value_; } template - void on_primary_key(const char *, ValueType &pk, typename std::enable_if::value && !std::is_same::value>::type* = 0) + void on_primary_key(const char *, ValueType &pk, std::enable_if_t && !std::is_same_v>* = nullptr) { value_ = pk; } @@ -35,13 +35,17 @@ public: template void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {} template - void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {} + void on_has_many_to_many(const char *, ContainerType &, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {} + template + void on_has_many_to_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {} + template + void on_has_many(const char *, ContainerType &, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {} template void on_has_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {} private: - any_type value_{}; + utils::database_type value_{}; }; } diff --git a/include/matador/query/intermediates/executable_query.hpp b/include/matador/query/intermediates/executable_query.hpp new file mode 100644 index 0000000..0b63440 --- /dev/null +++ b/include/matador/query/intermediates/executable_query.hpp @@ -0,0 +1,27 @@ +#ifndef EXECUTABLE_QUERY_HPP +#define EXECUTABLE_QUERY_HPP + +#include "query_intermediate.hpp" + +#include "../../utils/error.hpp" +#include "../../utils/result.hpp" + +namespace matador::sql { +class executor; +class statement; +} + +namespace matador::query { + +class executable_query : public query_intermediate { +public: + using query_intermediate::query_intermediate; + + [[nodiscard]] utils::result execute(const sql::executor &exec) const; + [[nodiscard]] utils::result prepare(const sql::executor &exec) const; + [[nodiscard]] std::string str(const sql::executor &exec) const; +}; + +} + +#endif //EXECUTABLE_QUERY_HPP diff --git a/include/matador/query/intermediates/fetchable_query.hpp b/include/matador/query/intermediates/fetchable_query.hpp new file mode 100644 index 0000000..23c1848 --- /dev/null +++ b/include/matador/query/intermediates/fetchable_query.hpp @@ -0,0 +1,82 @@ +#ifndef FETCHABLE_QUERY_HPP +#define FETCHABLE_QUERY_HPP + +#include "query_intermediate.hpp" + +#include "../query_compiler.hpp" + +#include "../../sql/query_result.hpp" +#include "../../sql/record.hpp" + +#include "../../utils/error.hpp" +#include "../../utils/result.hpp" + +namespace matador::sql { +class executor; +class statement; +} + +namespace matador::query { + +class fetchable_query : public query_intermediate +{ +protected: + using query_intermediate::query_intermediate; + +public: + template < class Type > + utils::result, utils::error> fetch_all(sql::executor &exec) + { + auto result = fetch(exec); + if (!result.is_ok()) { + return utils::error(result.err()); + } + + return utils::ok(sql::query_result(result.release())); + } + [[nodiscard]] utils::result, utils::error> fetch_all(const sql::executor &exec) const; + + template < class Type > + utils::result, utils::error> fetch_one(const sql::executor &exec) + { + auto result = fetch(exec); + if (!result.is_ok()) { + return utils::error(result.err()); + } + + auto objects = sql::query_result(result.release()); + auto first = objects.begin(); + if (first == objects.end()) { + return utils::ok(std::unique_ptr{nullptr}); + } + + return utils::ok(std::unique_ptr{first.release()}); + } + + [[nodiscard]] utils::result, utils::error> fetch_one(const sql::executor &exec) const; + + template + utils::result, utils::error> fetch_value(const sql::executor &exec) + { + const auto result = fetch_one(exec); + if (!result.is_ok()) { + return utils::failure(result.err()); + } + + if (result->has_value()) { + return utils::ok(std::optional{result->value().at(0).as().value()}); + } + return utils::ok(std::optional{std::nullopt}); + } + + [[nodiscard]] utils::result prepare(const sql::executor &exec) const; + + [[nodiscard]] std::string str(const sql::executor &exec) const; + +private: + [[nodiscard]] utils::result, utils::error> fetch(const sql::executor &exec) const; +}; + +} + +#endif //FETCHABLE_QUERY_HPP diff --git a/include/matador/query/intermediates/query_create_intermediate.hpp b/include/matador/query/intermediates/query_create_intermediate.hpp new file mode 100644 index 0000000..179fc3c --- /dev/null +++ b/include/matador/query/intermediates/query_create_intermediate.hpp @@ -0,0 +1,27 @@ +#ifndef QUERY_CREATE_INTERMEDIATE_HPP +#define QUERY_CREATE_INTERMEDIATE_HPP + +#include "matador/query/intermediates/query_intermediate.hpp" + +#include "matador/query/intermediates/executable_query.hpp" + +namespace matador::query { + +class query_create_intermediate : public query_intermediate +{ +public: + query_create_intermediate(); + + executable_query table(const sql::table &table, std::initializer_list columns); + executable_query table(const sql::table &table, const std::vector &columns); + // template + // executable_query table(const sql::table &table, const sql::schema &schema) + // { + // return this->table(table, column_definition_generator::generate(schema)); + // } +}; + +} + + +#endif //QUERY_CREATE_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_delete_from_intermediate.hpp b/include/matador/query/intermediates/query_delete_from_intermediate.hpp new file mode 100644 index 0000000..d8a2e60 --- /dev/null +++ b/include/matador/query/intermediates/query_delete_from_intermediate.hpp @@ -0,0 +1,29 @@ +#ifndef QUERY_DELETE_FROM_INTERMEDIATE_HPP +#define QUERY_DELETE_FROM_INTERMEDIATE_HPP + +#include "matador/query/intermediates/executable_query.hpp" + +#include "matador/query/basic_condition.hpp" + +#include "matador/query/intermediates/query_execute_where_intermediate.hpp" + +namespace matador::query { + +class query_delete_from_intermediate : public executable_query +{ +public: + using executable_query::executable_query; + + template + query_execute_where_intermediate where(const Condition &cond) + { + return where_clause(std::make_unique(std::move(cond))); + } + +private: + query_execute_where_intermediate where_clause(std::unique_ptr &&cond); +}; + +} + +#endif //QUERY_DELETE_FROM_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_delete_intermediate.hpp b/include/matador/query/intermediates/query_delete_intermediate.hpp new file mode 100644 index 0000000..ccf0577 --- /dev/null +++ b/include/matador/query/intermediates/query_delete_intermediate.hpp @@ -0,0 +1,20 @@ +#ifndef QUERY_DELETE_INTERMEDIATE_HPP +#define QUERY_DELETE_INTERMEDIATE_HPP + +#include "matador/query/intermediates/query_intermediate.hpp" + +namespace matador::query { + +class query_delete_from_intermediate; + +class query_delete_intermediate : public query_intermediate +{ +public: + query_delete_intermediate(); + + query_delete_from_intermediate from(const sql::table &table); +}; + +} + +#endif //QUERY_DELETE_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_drop_intermediate.hpp b/include/matador/query/intermediates/query_drop_intermediate.hpp new file mode 100644 index 0000000..2940f8e --- /dev/null +++ b/include/matador/query/intermediates/query_drop_intermediate.hpp @@ -0,0 +1,18 @@ +#ifndef QUERY_DROP_INTERMEDIATE_HPP +#define QUERY_DROP_INTERMEDIATE_HPP + +#include "matador/query/intermediates/executable_query.hpp" + +namespace matador::query { + +class query_drop_intermediate : query_intermediate +{ +public: + query_drop_intermediate(); + + executable_query table(const sql::table &table); +}; + +} + +#endif //QUERY_DROP_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_execute_limit_intermediate.hpp b/include/matador/query/intermediates/query_execute_limit_intermediate.hpp new file mode 100644 index 0000000..1b7d921 --- /dev/null +++ b/include/matador/query/intermediates/query_execute_limit_intermediate.hpp @@ -0,0 +1,20 @@ +#ifndef QUERY_EXECUTE_LIMIT_INTERMEDIATE_HPP +#define QUERY_EXECUTE_LIMIT_INTERMEDIATE_HPP + +#include "matador/query/intermediates/executable_query.hpp" + +namespace matador::query { + +class query_execute_offset_intermediate; + +class query_execute_limit_intermediate : public executable_query +{ +public: + using executable_query::executable_query; + + query_execute_offset_intermediate offset(size_t offset); +}; + +} + +#endif //QUERY_EXECUTE_LIMIT_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_execute_offset_intermediate.hpp b/include/matador/query/intermediates/query_execute_offset_intermediate.hpp new file mode 100644 index 0000000..dc76923 --- /dev/null +++ b/include/matador/query/intermediates/query_execute_offset_intermediate.hpp @@ -0,0 +1,20 @@ +#ifndef QUERY_EXECUTE_OFFSET_INTERMEDIATE_HPP +#define QUERY_EXECUTE_OFFSET_INTERMEDIATE_HPP + +#include "matador/query/intermediates/executable_query.hpp" + +namespace matador::query { + +class query_execute_limit_intermediate; + +class query_execute_offset_intermediate : public executable_query +{ +public: + using executable_query::executable_query; + + query_execute_limit_intermediate limit(size_t limit); +}; + +} + +#endif //QUERY_EXECUTE_OFFSET_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_execute_order_by_intermediate.hpp b/include/matador/query/intermediates/query_execute_order_by_intermediate.hpp new file mode 100644 index 0000000..52e2044 --- /dev/null +++ b/include/matador/query/intermediates/query_execute_order_by_intermediate.hpp @@ -0,0 +1,21 @@ +#ifndef QUERY_EXECUTE_ORDER_BY_INTERMEDIATE_HPP +#define QUERY_EXECUTE_ORDER_BY_INTERMEDIATE_HPP + +#include "matador/query/intermediates/query_intermediate.hpp" + +namespace matador::query { + +class query_execute_order_direction_intermediate; + +class query_execute_order_by_intermediate : public query_intermediate +{ +public: + using query_intermediate::query_intermediate; + + query_execute_order_direction_intermediate asc(); + query_execute_order_direction_intermediate desc(); +}; + +} + +#endif //QUERY_EXECUTE_ORDER_BY_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_execute_order_direction_intermediate.hpp b/include/matador/query/intermediates/query_execute_order_direction_intermediate.hpp new file mode 100644 index 0000000..0986a35 --- /dev/null +++ b/include/matador/query/intermediates/query_execute_order_direction_intermediate.hpp @@ -0,0 +1,20 @@ +#ifndef QUERY_EXECUTE_ORDER_DIRECTION_INTERMEDIATE_HPP +#define QUERY_EXECUTE_ORDER_DIRECTION_INTERMEDIATE_HPP + +#include "matador/query/intermediates/executable_query.hpp" + +namespace matador::query { + +class query_execute_limit_intermediate; + +class query_execute_order_direction_intermediate : public executable_query +{ +public: + using executable_query::executable_query; + + query_execute_limit_intermediate limit(size_t limit); +}; + +} + +#endif //QUERY_EXECUTE_ORDER_DIRECTION_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_execute_where_intermediate.hpp b/include/matador/query/intermediates/query_execute_where_intermediate.hpp new file mode 100644 index 0000000..70b8a67 --- /dev/null +++ b/include/matador/query/intermediates/query_execute_where_intermediate.hpp @@ -0,0 +1,22 @@ +#ifndef QUERY_EXECUTE_WHERE_INTERMEDIATE_HPP +#define QUERY_EXECUTE_WHERE_INTERMEDIATE_HPP + +#include "matador/query/intermediates/executable_query.hpp" + +namespace matador::query { + +class query_execute_limit_intermediate; +class query_execute_order_by_intermediate; + +class query_execute_where_intermediate : public executable_query +{ +public: + using executable_query::executable_query; + + query_execute_limit_intermediate limit(size_t limit); + query_execute_order_by_intermediate order_by(const sql::column &col); +}; + +} + +#endif //QUERY_EXECUTE_WHERE_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_from_intermediate.hpp b/include/matador/query/intermediates/query_from_intermediate.hpp new file mode 100644 index 0000000..8ef3b58 --- /dev/null +++ b/include/matador/query/intermediates/query_from_intermediate.hpp @@ -0,0 +1,40 @@ +#ifndef QUERY_FROM_INTERMEDIATE_HPP +#define QUERY_FROM_INTERMEDIATE_HPP + +#include "matador/query/intermediates/fetchable_query.hpp" +#include "matador/query/join_data.hpp" + +#include "matador/query/intermediates/query_where_intermediate.hpp" + +namespace matador::query { + +class query_join_intermediate; + +class query_from_intermediate : public fetchable_query +{ +public: + using fetchable_query::fetchable_query; + + query_join_intermediate join_left(const sql::table &t); + query_from_intermediate join_left(join_data &data); + query_from_intermediate join_left(std::vector &data_vector); + + template + query_where_intermediate where(const Condition &cond) + { + return where_clause(std::make_unique(std::move(cond))); + } + query_where_intermediate where(std::unique_ptr &&cond) + { + return where_clause(std::move(cond)); + } + query_group_by_intermediate group_by(const sql::column &col); + query_order_by_intermediate order_by(const sql::column &col); + +private: + query_where_intermediate where_clause(std::unique_ptr &&cond); +}; + +} + +#endif //QUERY_FROM_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_group_by_intermediate.hpp b/include/matador/query/intermediates/query_group_by_intermediate.hpp new file mode 100644 index 0000000..1fa7d83 --- /dev/null +++ b/include/matador/query/intermediates/query_group_by_intermediate.hpp @@ -0,0 +1,20 @@ +#ifndef QUERY_GROUP_BY_INTERMEDIATE_HPP +#define QUERY_GROUP_BY_INTERMEDIATE_HPP + +#include "matador/query/intermediates/fetchable_query.hpp" + +namespace matador::query { + +class query_order_by_intermediate; + +class query_group_by_intermediate : public fetchable_query +{ +public: + using fetchable_query::fetchable_query; + + query_order_by_intermediate order_by(const sql::column &col); +}; + +} + +#endif //QUERY_GROUP_BY_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_insert_intermediate.hpp b/include/matador/query/intermediates/query_insert_intermediate.hpp new file mode 100644 index 0000000..b3d53b6 --- /dev/null +++ b/include/matador/query/intermediates/query_insert_intermediate.hpp @@ -0,0 +1,27 @@ +#ifndef QUERY_INSERT_INTERMEDIATE_HPP +#define QUERY_INSERT_INTERMEDIATE_HPP + +#include "matador/query/intermediates/query_intermediate.hpp" + +namespace matador::query { + +class query_into_intermediate; + +class query_insert_intermediate : public query_intermediate +{ +public: + query_insert_intermediate(); + + // template + // query_into_intermediate into(const sql::table &table, const sql::schema &schema) { + // return into(table, column_generator::generate(schema)); + // } + query_into_intermediate into(const sql::table &table, std::initializer_list columns); + query_into_intermediate into(const sql::table &table, std::vector &&columns); + query_into_intermediate into(const sql::table &table, const std::vector &column_names); + query_into_intermediate into(const sql::table &table); +}; + +} + +#endif //QUERY_INSERT_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_intermediate.hpp b/include/matador/query/intermediates/query_intermediate.hpp new file mode 100644 index 0000000..9cee133 --- /dev/null +++ b/include/matador/query/intermediates/query_intermediate.hpp @@ -0,0 +1,21 @@ +#ifndef QUERY_INTERMEDIATE_HPP +#define QUERY_INTERMEDIATE_HPP + +#include "matador/query/query_data.hpp" + +#include + +namespace matador::query { + +class query_intermediate { +public: + query_intermediate(); + query_intermediate(const std::shared_ptr &context); // NOLINT(*-explicit-constructor) + +protected: + std::shared_ptr context_; +}; + +} + +#endif //QUERY_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_into_intermediate.hpp b/include/matador/query/intermediates/query_into_intermediate.hpp new file mode 100644 index 0000000..0d38c3c --- /dev/null +++ b/include/matador/query/intermediates/query_into_intermediate.hpp @@ -0,0 +1,43 @@ +#ifndef QUERY_INTO_INTERMEDIATE_HPP +#define QUERY_INTO_INTERMEDIATE_HPP + +#include "matador/query/intermediates/executable_query.hpp" + +#include "matador/query/value_extractor.hpp" + +#include "matador/utils/placeholder.hpp" + +namespace matador::query { + +// template < class Type > +// std::vector as_placeholder(const Type &obj) +// { + // placeholder_generator generator; + // access::process(generator, obj); + + // return generator.placeholder_values; +// } + +class query_into_intermediate : public query_intermediate +{ +public: + using query_intermediate::query_intermediate; + + executable_query values(std::initializer_list> values); + executable_query values(std::vector> &&values); + // template + // executable_query values() + // { + // Type obj; + // return values(std::move(as_placeholder(obj))); + // } + template + executable_query values(const Type &obj) + { + return values(std::move(value_extractor::extract(obj))); + } +}; + +} + +#endif //QUERY_INTO_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_join_intermediate.hpp b/include/matador/query/intermediates/query_join_intermediate.hpp new file mode 100644 index 0000000..caf79d4 --- /dev/null +++ b/include/matador/query/intermediates/query_join_intermediate.hpp @@ -0,0 +1,32 @@ +#ifndef QUERY_JOIN_INTERMEDIATE_HPP +#define QUERY_JOIN_INTERMEDIATE_HPP + +#include "matador/query/intermediates/query_intermediate.hpp" +#include "matador/query/intermediates/query_from_intermediate.hpp" + +namespace matador::query { + +using query_on_intermediate = query_from_intermediate; + +class query_join_intermediate : public query_intermediate +{ +public: + using query_intermediate::query_intermediate; + + template + query_on_intermediate on(const Condition &cond) + { + return on_clause(std::make_unique(std::move(cond))); + } + query_on_intermediate on(std::unique_ptr &&cond) + { + return on_clause(std::move(cond)); + } + +private: + query_on_intermediate on_clause(std::unique_ptr &&cond); +}; + +} + +#endif //QUERY_JOIN_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_limit_intermediate.hpp b/include/matador/query/intermediates/query_limit_intermediate.hpp new file mode 100644 index 0000000..2006121 --- /dev/null +++ b/include/matador/query/intermediates/query_limit_intermediate.hpp @@ -0,0 +1,20 @@ +#ifndef QUERY_LIMIT_INTERMEDIATE_HPP +#define QUERY_LIMIT_INTERMEDIATE_HPP + +#include "matador/query/intermediates/fetchable_query.hpp" + +namespace matador::query { + +class query_offset_intermediate; + +class query_limit_intermediate : public fetchable_query +{ +public: + using fetchable_query::fetchable_query; + + query_offset_intermediate offset(size_t offset); +}; + +} + +#endif //QUERY_LIMIT_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_offset_intermediate.hpp b/include/matador/query/intermediates/query_offset_intermediate.hpp new file mode 100644 index 0000000..b04c909 --- /dev/null +++ b/include/matador/query/intermediates/query_offset_intermediate.hpp @@ -0,0 +1,20 @@ +#ifndef QUERY_OFFSET_INTERMEDIATE_HPP +#define QUERY_OFFSET_INTERMEDIATE_HPP + +#include "matador/query/intermediates/fetchable_query.hpp" + +namespace matador::query { + +class query_limit_intermediate; + +class query_offset_intermediate : public fetchable_query +{ +public: + using fetchable_query::fetchable_query; + + query_limit_intermediate limit(size_t limit); +}; + +} + +#endif //QUERY_OFFSET_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_order_by_intermediate.hpp b/include/matador/query/intermediates/query_order_by_intermediate.hpp new file mode 100644 index 0000000..a51c5f1 --- /dev/null +++ b/include/matador/query/intermediates/query_order_by_intermediate.hpp @@ -0,0 +1,21 @@ +#ifndef QUERY_ORDER_BY_INTERMEDIATE_HPP +#define QUERY_ORDER_BY_INTERMEDIATE_HPP + +#include "matador/query/intermediates/query_intermediate.hpp" + +namespace matador::query { + +class query_order_direction_intermediate; + +class query_order_by_intermediate : public query_intermediate +{ +public: + using query_intermediate::query_intermediate; + + query_order_direction_intermediate asc(); + query_order_direction_intermediate desc(); +}; + +} + +#endif //QUERY_ORDER_BY_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_order_direction_intermediate.hpp b/include/matador/query/intermediates/query_order_direction_intermediate.hpp new file mode 100644 index 0000000..ae117ed --- /dev/null +++ b/include/matador/query/intermediates/query_order_direction_intermediate.hpp @@ -0,0 +1,20 @@ +#ifndef QUERY_ORDER_DIRECTION_INTERMEDIATE_HPP +#define QUERY_ORDER_DIRECTION_INTERMEDIATE_HPP + +#include "matador/query/intermediates/fetchable_query.hpp" + +namespace matador::query { + +class query_limit_intermediate; + +class query_order_direction_intermediate : public fetchable_query +{ +public: + using fetchable_query::fetchable_query; + + query_limit_intermediate limit(size_t limit); +}; + +} + +#endif //QUERY_ORDER_DIRECTION_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_select_intermediate.hpp b/include/matador/query/intermediates/query_select_intermediate.hpp new file mode 100644 index 0000000..8702e31 --- /dev/null +++ b/include/matador/query/intermediates/query_select_intermediate.hpp @@ -0,0 +1,20 @@ +#ifndef QUERY_SELECT_INTERMEDIATE_HPP +#define QUERY_SELECT_INTERMEDIATE_HPP + +#include "matador/query/intermediates/query_intermediate.hpp" + +namespace matador::query { + +class query_from_intermediate; + +class query_select_intermediate : public query_intermediate +{ +public: + explicit query_select_intermediate(const std::vector& columns); + + query_from_intermediate from(const sql::table& t); +}; + +} + +#endif //QUERY_SELECT_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_set_intermediate.hpp b/include/matador/query/intermediates/query_set_intermediate.hpp new file mode 100644 index 0000000..6486f4f --- /dev/null +++ b/include/matador/query/intermediates/query_set_intermediate.hpp @@ -0,0 +1,29 @@ +#ifndef QUERY_SET_INTERMEDIATE_HPP +#define QUERY_SET_INTERMEDIATE_HPP + +#include "matador/query/intermediates/executable_query.hpp" + +#include "matador/query/basic_condition.hpp" + +#include "matador/query/intermediates/query_execute_where_intermediate.hpp" + +namespace matador::query { + +class query_set_intermediate : public executable_query +{ +public: + using executable_query::executable_query; + + template + query_execute_where_intermediate where(const Condition &cond) + { + return where_clause(std::make_unique(std::move(cond))); + } + +private: + query_execute_where_intermediate where_clause(std::unique_ptr &&cond); +}; + +} + +#endif //QUERY_SET_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_update_intermediate.hpp b/include/matador/query/intermediates/query_update_intermediate.hpp new file mode 100644 index 0000000..99b9d96 --- /dev/null +++ b/include/matador/query/intermediates/query_update_intermediate.hpp @@ -0,0 +1,45 @@ +#ifndef QUERY_UPDATE_INTERMEDIATE_HPP +#define QUERY_UPDATE_INTERMEDIATE_HPP + +#include "matador/query/intermediates/query_intermediate.hpp" + +#include "matador/query/intermediates/query_set_intermediate.hpp" + +#include "matador/query/key_value_generator.hpp" + +#include "matador/query/internal/key_value_pair.hpp" + +namespace matador::query { + +// template < class Type > +// std::vector as_key_value_placeholder(const Type &obj) +// { +// placeholder_key_value_generator generator; +// access::process(generator, obj); +// +// return generator.placeholder_values; +// } + +class query_update_intermediate : public query_intermediate +{ +public: + explicit query_update_intermediate(const sql::table& table); + + query_set_intermediate set(std::initializer_list columns); + query_set_intermediate set(std::vector &&columns); + // template + // query_set_intermediate set() + // { + // Type obj; + // return set(std::move(as_key_value_placeholder(obj))); + // } + template + query_set_intermediate set(const Type &obj) + { + return set(key_value_generator::generate(obj)); + } +}; + +} + +#endif //QUERY_UPDATE_INTERMEDIATE_HPP diff --git a/include/matador/query/intermediates/query_where_intermediate.hpp b/include/matador/query/intermediates/query_where_intermediate.hpp new file mode 100644 index 0000000..15b990f --- /dev/null +++ b/include/matador/query/intermediates/query_where_intermediate.hpp @@ -0,0 +1,22 @@ +#ifndef QUERY_WHERE_INTERMEDIATE_HPP +#define QUERY_WHERE_INTERMEDIATE_HPP + +#include "matador/query/intermediates/fetchable_query.hpp" + +namespace matador::query { + +class query_group_by_intermediate; +class query_order_by_intermediate; + +class query_where_intermediate : public fetchable_query +{ +public: + using fetchable_query::fetchable_query; + + query_group_by_intermediate group_by(const sql::column &col); + query_order_by_intermediate order_by(const sql::column &col); +}; + +} + +#endif //QUERY_WHERE_INTERMEDIATE_HPP diff --git a/include/matador/query/internal/basic_type_to_string_visitor.hpp b/include/matador/query/internal/basic_type_to_string_visitor.hpp new file mode 100644 index 0000000..6087c75 --- /dev/null +++ b/include/matador/query/internal/basic_type_to_string_visitor.hpp @@ -0,0 +1,49 @@ +#ifndef QUERY_ANY_TYPE_TO_STRING_VISITOR_HPP +#define QUERY_ANY_TYPE_TO_STRING_VISITOR_HPP + +#include "matador/utils/types.hpp" + +#include "matador/query/attribute_string_writer.hpp" + +#include + +namespace matador { +class date; +class time; +} + +namespace matador::sql { +class dialect; +struct query_context; +} + +namespace matador::query::internal { + +struct basic_type_to_string_visitor +{ + explicit basic_type_to_string_visitor(attribute_string_writer &writer, sql::query_context &query); + + void operator()(const int8_t &x) { result = writer->to_string(x); } + void operator()(const int16_t &x) { result = writer->to_string(x); } + void operator()(const int32_t &x) { result = writer->to_string(x); } + void operator()(const int64_t &x) { result = writer->to_string(x); } + void operator()(const uint8_t &x) { result = writer->to_string(x); } + void operator()(const uint16_t &x) { result = writer->to_string(x); } + void operator()(const uint32_t &x) { result = writer->to_string(x); } + void operator()(const uint64_t &x) { result = writer->to_string(x); } + void operator()(const bool &x) { result = writer->to_string(x); } + void operator()(const float &x) { result = writer->to_string(x); } + void operator()(const double &x) { result = writer->to_string(x); } + void operator()(const char *x) { result = writer->to_string(x); } + void operator()(const std::string &x) { result = writer->to_string(x); } + void operator()(const matador::date &x) { result = writer->to_string(x); } + void operator()(const matador::time &x) { result = writer->to_string(x); } + void operator()(const utils::blob &x) { result = writer->to_string(x); } + + attribute_string_writer *writer{}; + sql::query_context &query; + std::string result; +}; + +} +#endif //QUERY_ANY_TYPE_TO_STRING_VISITOR_HPP diff --git a/include/matador/query/internal/key_value_pair.hpp b/include/matador/query/internal/key_value_pair.hpp new file mode 100644 index 0000000..bdea78d --- /dev/null +++ b/include/matador/query/internal/key_value_pair.hpp @@ -0,0 +1,25 @@ +#ifndef QUERY_KEY_VALUE_PAIR_HPP +#define QUERY_KEY_VALUE_PAIR_HPP + +#include "matador/sql/column.hpp" + +#include "matador/utils/types.hpp" + +namespace matador::query::internal { + +class key_value_pair { +public: + key_value_pair(const sql::column &col, utils::database_type value); + key_value_pair(std::string name, utils::database_type value); + key_value_pair(const char *name, utils::database_type value); + + [[nodiscard]] const std::string& name() const; + [[nodiscard]] const utils::database_type& value() const; + +private: + std::string name_; + utils::database_type value_; +}; + +} +#endif //QUERY_KEY_VALUE_PAIR_HPP diff --git a/include/matador/sql/query_parts.hpp b/include/matador/query/internal/query_parts.hpp similarity index 65% rename from include/matador/sql/query_parts.hpp rename to include/matador/query/internal/query_parts.hpp index 7e15741..0dd2307 100644 --- a/include/matador/sql/query_parts.hpp +++ b/include/matador/query/internal/query_parts.hpp @@ -1,39 +1,41 @@ #ifndef QUERY_QUERY_PARTS_HPP #define QUERY_QUERY_PARTS_HPP -#include "matador/sql/basic_condition.hpp" -#include "matador/sql/query_part_visitor.hpp" +#include "matador/query/query_part_visitor.hpp" +#include "matador/query/basic_condition.hpp" + +#include "matador/query/internal/key_value_pair.hpp" +#include "matador/query/query_part.hpp" + #include "matador/sql/column.hpp" #include "matador/sql/column_definition.hpp" -#include "matador/sql/key_value_pair.hpp" -#include "matador/sql/query_part.hpp" #include "matador/sql/table.hpp" +#include "matador/utils/placeholder.hpp" + #include -namespace matador::sql { - -class basic_condition; +namespace matador::query::internal { /** * Represents the SQL SELECT part */ -class query_select_part : public query_part +class query_select_part final : public query_part { public: - explicit query_select_part(std::vector columns); + explicit query_select_part(std::vector columns); void accept(query_part_visitor &visitor) override; - [[nodiscard]] const std::vector& columns() const; + [[nodiscard]] const std::vector& columns() const; private: - std::vector columns_; + std::vector columns_; }; /** * Represents the SQL FROM part */ -class query_from_part : public query_part +class query_from_part final : public query_part { public: explicit query_from_part(sql::table t); @@ -47,7 +49,7 @@ private: sql::table table_; }; -class query_join_part : public query_part +class query_join_part final : public query_part { public: explicit query_join_part(sql::table t); @@ -61,12 +63,12 @@ private: sql::table table_; }; -class query_on_part : public query_part +class query_on_part final : public query_part { public: template < class Condition > explicit query_on_part(const Condition &cond) - : query_part(dialect::token_t::ON) + : query_part(sql::dialect_token::ON) , condition_(new Condition(cond)) {} explicit query_on_part(std::unique_ptr &&cond); @@ -79,12 +81,12 @@ private: std::unique_ptr condition_; }; -class query_where_part : public query_part +class query_where_part final : public query_part { public: template < class Condition > explicit query_where_part(const Condition &cond) - : query_part(dialect::token_t::WHERE) + : query_part(sql::dialect_token::WHERE) , condition_(new Condition(cond)) {} explicit query_where_part(std::unique_ptr &&cond); @@ -100,13 +102,13 @@ private: class query_table_name_part : public query_part { protected: - explicit query_table_name_part(sql::dialect::token_t token, std::string table_name); + explicit query_table_name_part(sql::dialect_token token, std::string table_name); protected: std::string table_name_; }; -class query_group_by_part : public query_part +class query_group_by_part final : public query_part { public: explicit query_group_by_part(sql::column col); @@ -120,7 +122,7 @@ private: sql::column column_; }; -class query_order_by_part : public query_part +class query_order_by_part final : public query_part { public: explicit query_order_by_part(sql::column col); @@ -134,7 +136,7 @@ private: sql::column column_; }; -class query_order_by_asc_part : public query_part +class query_order_by_asc_part final : public query_part { public: query_order_by_asc_part(); @@ -143,7 +145,7 @@ private: void accept(query_part_visitor &visitor) override; }; -class query_order_by_desc_part : public query_part +class query_order_by_desc_part final : public query_part { public: query_order_by_desc_part(); @@ -152,7 +154,7 @@ private: void accept(query_part_visitor &visitor) override; }; -class query_offset_part : public query_part +class query_offset_part final : public query_part { public: explicit query_offset_part(size_t offset); @@ -166,7 +168,7 @@ private: size_t offset_; }; -class query_limit_part : public query_part +class query_limit_part final : public query_part { public: explicit query_limit_part(size_t limit); @@ -180,7 +182,7 @@ private: size_t limit_; }; -class query_insert_part : public query_part +class query_insert_part final : public query_part { public: query_insert_part(); @@ -189,39 +191,39 @@ private: void accept(query_part_visitor &visitor) override; }; -class query_into_part : public query_part +class query_into_part final : public query_part { public: query_into_part(sql::table t, std::vector columns); [[nodiscard]] const sql::table& table() const; - [[nodiscard]] const std::vector& columns() const; + [[nodiscard]] const std::vector& columns() const; private: void accept(query_part_visitor &visitor) override; private: sql::table table_; - std::vector columns_; + std::vector columns_; }; /** * Represents the SQL VALUES part */ -class query_values_part : public query_part +class query_values_part final : public query_part { public: - explicit query_values_part(std::vector &&values); + explicit query_values_part(std::vector> &&values); - [[nodiscard]] const std::vector& values() const; + [[nodiscard]] const std::vector>& values() const; private: void accept(query_part_visitor &visitor) override; private: - std::vector values_; + std::vector> values_; }; -class query_update_part : public query_part +class query_update_part final : public query_part { public: explicit query_update_part(sql::table table); @@ -235,21 +237,21 @@ private: sql::table table_; }; -class query_set_part : public query_part +class query_set_part final : public query_part { public: - explicit query_set_part(const std::vector& key_value_pairs); + explicit query_set_part(const std::vector& key_value_pairs); - [[nodiscard]] const std::vector& key_values() const; + [[nodiscard]] const std::vector& key_values() const; private: void accept(query_part_visitor &visitor) override; private: - std::vector key_value_pairs_; + std::vector key_value_pairs_; }; -class query_delete_part : public query_part +class query_delete_part final : public query_part { public: query_delete_part(); @@ -258,10 +260,10 @@ private: void accept(query_part_visitor &visitor) override; }; -class query_delete_from_part : public query_part +class query_delete_from_part final : public query_part { public: - query_delete_from_part(sql::table table); + explicit query_delete_from_part(sql::table table); [[nodiscard]] const sql::table& table() const; @@ -272,7 +274,7 @@ private: sql::table table_; }; -class query_create_part : public query_part +class query_create_part final : public query_part { public: query_create_part(); @@ -281,7 +283,7 @@ private: void accept(query_part_visitor &visitor) override; }; -class query_create_table_part : public query_part +class query_create_table_part final : public query_part { public: query_create_table_part(sql::table table, std::vector columns); @@ -297,7 +299,7 @@ private: std::vector columns_; }; -class query_drop_part : public query_part +class query_drop_part final : public query_part { public: query_drop_part(); @@ -306,7 +308,7 @@ private: void accept(query_part_visitor &visitor) override; }; -class query_drop_table_part : public query_part +class query_drop_table_part final : public query_part { public: explicit query_drop_table_part(sql::table table); diff --git a/include/matador/query/join_data.hpp b/include/matador/query/join_data.hpp new file mode 100644 index 0000000..5f25358 --- /dev/null +++ b/include/matador/query/join_data.hpp @@ -0,0 +1,19 @@ +#ifndef JOIN_DATA_HPP +#define JOIN_DATA_HPP + +#include "matador/query/basic_condition.hpp" + +#include "matador/sql/table.hpp" + +#include + +namespace matador::query { + +struct join_data +{ + std::shared_ptr join_table; + std::unique_ptr condition; +}; + +} +#endif //JOIN_DATA_HPP diff --git a/include/matador/sql/key_value_generator.hpp b/include/matador/query/key_value_generator.hpp similarity index 75% rename from include/matador/sql/key_value_generator.hpp rename to include/matador/query/key_value_generator.hpp index e67dc16..9303d33 100644 --- a/include/matador/sql/key_value_generator.hpp +++ b/include/matador/query/key_value_generator.hpp @@ -1,28 +1,30 @@ #ifndef QUERY_KEY_VALUE_GENERATOR_HPP #define QUERY_KEY_VALUE_GENERATOR_HPP -#include "matador/sql/fk_value_extractor.hpp" -#include "matador/sql/key_value_pair.hpp" +#include "matador/query/fk_value_extractor.hpp" +#include "matador/query/internal/key_value_pair.hpp" + +#include "matador/utils/foreign_attributes.hpp" #include -namespace matador::sql { +namespace matador::query { class key_value_generator { private: public: - explicit key_value_generator(std::vector &result) : result_(result) {} + explicit key_value_generator(std::vector &result) : result_(result) {} public: template < class Type > - static std::vector generate(const Type &obj) + static std::vector generate(const Type &obj) { - std::vector result; + std::vector result; key_value_generator generator(result); - matador::utils::access::process(generator, obj); + access::process(generator, obj); - return std::move(result); + return result; } template < class V > @@ -56,7 +58,7 @@ public: private: detail::fk_value_extractor fk_value_extractor_; - std::vector &result_; + std::vector &result_; }; } diff --git a/include/matador/query/query.hpp b/include/matador/query/query.hpp new file mode 100644 index 0000000..182b774 --- /dev/null +++ b/include/matador/query/query.hpp @@ -0,0 +1,35 @@ +#ifndef QUERY_QUERY_HPP +#define QUERY_QUERY_HPP + +#include "matador/query/query_intermediates.hpp" + +namespace matador::sql { +class connection; +} +namespace matador::query { + +sql::column alias(const std::string &column, const std::string &as); +sql::column alias(sql::column &&col, const std::string &as); +sql::column count(const std::string &column); +sql::column count_all(); + +class query +{ +public: + [[nodiscard]] static query_create_intermediate create(); + [[nodiscard]] static query_drop_intermediate drop(); + [[nodiscard]] static query_select_intermediate select(std::initializer_list columns); + [[nodiscard]] static query_select_intermediate select(const std::vector& columns); + [[nodiscard]] static query_select_intermediate select(const std::vector &column_names); + [[nodiscard]] static query_select_intermediate select(std::vector columns, std::initializer_list additional_columns); + // template + // [[nodiscard]] static query_select_intermediate select(const sql::schema &schema) { + // return select(sql::column_generator::generate(schema)); + // } + [[nodiscard]] static query_insert_intermediate insert(); + [[nodiscard]] static query_update_intermediate update(const sql::table &table); + [[nodiscard]] static query_delete_intermediate remove(); +}; + +} +#endif //QUERY_QUERY_HPP diff --git a/include/matador/query/query_compiler.hpp b/include/matador/query/query_compiler.hpp new file mode 100644 index 0000000..ecffc2a --- /dev/null +++ b/include/matador/query/query_compiler.hpp @@ -0,0 +1,65 @@ +#ifndef QUERY_QUERY_COMPILER_HPP +#define QUERY_QUERY_COMPILER_HPP + +#include "matador/query/query_part_visitor.hpp" +#include "matador/query/query_data.hpp" + +#include "matador/sql/query_context.hpp" + +namespace matador::sql { +class connection_impl; +class dialect; +} + +namespace matador::query { + +struct query_data; + +class query_compiler final : public query_part_visitor +{ +public: + sql::query_context compile(const query_data &data, + const sql::dialect &d, + std::optional> conn); + +protected: + void visit(internal::query_select_part &select_part) override; + void visit(internal::query_from_part &from_part) override; + void visit(internal::query_join_part &join_part) override; + void visit(internal::query_on_part &on_part) override; + void visit(internal::query_where_part &where_part) override; + void visit(internal::query_group_by_part &group_by_part) override; + void visit(internal::query_order_by_part &order_by_part) override; + void visit(internal::query_order_by_asc_part &order_by_asc_part) override; + void visit(internal::query_order_by_desc_part &order_by_desc_part) override; + void visit(internal::query_offset_part &offset_part) override; + void visit(internal::query_limit_part &limit_part) override; + void visit(internal::query_insert_part &insert_part) override; + void visit(internal::query_into_part &into_part) override; + void visit(internal::query_values_part &values_part) override; + + void visit(internal::query_update_part &update_part) override; + void visit(internal::query_set_part &set_part) override; + + void visit(internal::query_delete_part &delete_part) override; + void visit(internal::query_delete_from_part &delete_from_part) override; + + void visit(internal::query_create_part &create_part) override; + void visit(internal::query_create_table_part &create_table_part) override; + + void visit(internal::query_drop_part &drop_part) override; + void visit(internal::query_drop_table_part &drop_table_part) override; + + static std::string build_table_name(sql::dialect_token token, const sql::dialect &d, const sql::table& t); + +protected: + const query_data *data_{}; + sql::query_context query_; + size_t table_index{0}; + const sql::dialect *dialect_{nullptr}; + std::optional> connection_{}; +}; + +} + +#endif //QUERY_QUERY_COMPILER_HPP diff --git a/include/matador/query/query_data.hpp b/include/matador/query/query_data.hpp new file mode 100644 index 0000000..6029c43 --- /dev/null +++ b/include/matador/query/query_data.hpp @@ -0,0 +1,21 @@ +#ifndef QUERY_DATA_HPP +#define QUERY_DATA_HPP + +#include +#include + +#include "matador/sql/column_definition.hpp" +#include "matador/sql/table.hpp" + +#include "matador/query/query_part.hpp" + +namespace matador::query { +struct query_data +{ + std::vector> parts{}; + std::vector columns{}; + std::unordered_map tables{}; +}; +} + +#endif //QUERY_DATA_HPP diff --git a/include/matador/query/query_intermediates.hpp b/include/matador/query/query_intermediates.hpp new file mode 100644 index 0000000..45ea28d --- /dev/null +++ b/include/matador/query/query_intermediates.hpp @@ -0,0 +1,30 @@ +#ifndef QUERY_QUERY_INTERMEDIATES_HPP +#define QUERY_QUERY_INTERMEDIATES_HPP + +#include "matador/query/intermediates/executable_query.hpp" +#include "matador/query/intermediates/fetchable_query.hpp" +#include "matador/query/intermediates/query_create_intermediate.hpp" +#include "matador/query/intermediates/query_delete_from_intermediate.hpp" +#include "matador/query/intermediates/query_delete_intermediate.hpp" +#include "matador/query/intermediates/query_drop_intermediate.hpp" +#include "matador/query/intermediates/query_execute_limit_intermediate.hpp" +#include "matador/query/intermediates/query_execute_offset_intermediate.hpp" +#include "matador/query/intermediates/query_execute_order_by_intermediate.hpp" +#include "matador/query/intermediates/query_execute_order_direction_intermediate.hpp" +#include "matador/query/intermediates/query_execute_where_intermediate.hpp" +#include "matador/query/intermediates/query_from_intermediate.hpp" +#include "matador/query/intermediates/query_group_by_intermediate.hpp" +#include "matador/query/intermediates/query_insert_intermediate.hpp" +#include "matador/query/intermediates/query_intermediate.hpp" +#include "matador/query/intermediates/query_into_intermediate.hpp" +#include "matador/query/intermediates/query_join_intermediate.hpp" +#include "matador/query/intermediates/query_limit_intermediate.hpp" +#include "matador/query/intermediates/query_offset_intermediate.hpp" +#include "matador/query/intermediates/query_order_by_intermediate.hpp" +#include "matador/query/intermediates/query_order_direction_intermediate.hpp" +#include "matador/query/intermediates/query_select_intermediate.hpp" +#include "matador/query/intermediates/query_set_intermediate.hpp" +#include "matador/query/intermediates/query_update_intermediate.hpp" +#include "matador/query/intermediates/query_where_intermediate.hpp" + +#endif //QUERY_QUERY_INTERMEDIATES_HPP diff --git a/include/matador/sql/query_part.hpp b/include/matador/query/query_part.hpp similarity index 57% rename from include/matador/sql/query_part.hpp rename to include/matador/query/query_part.hpp index 4e6266b..a9df5a5 100644 --- a/include/matador/sql/query_part.hpp +++ b/include/matador/query/query_part.hpp @@ -1,25 +1,25 @@ #ifndef QUERY_QUERY_PART_HPP #define QUERY_QUERY_PART_HPP -#include "matador/sql/dialect.hpp" +#include "matador/sql/dialect_token.hpp" -namespace matador::sql { +namespace matador::query { class query_part_visitor; class query_part { protected: - explicit query_part(sql::dialect::token_t token); + explicit query_part(sql::dialect_token token); public: virtual ~query_part() = default; virtual void accept(query_part_visitor &visitor) = 0; - [[nodiscard]] dialect::token_t token() const; + [[nodiscard]] sql::dialect_token token() const; protected: - sql::dialect::token_t token_; + sql::dialect_token token_; }; } diff --git a/include/matador/query/query_part_visitor.hpp b/include/matador/query/query_part_visitor.hpp new file mode 100644 index 0000000..bc59bc4 --- /dev/null +++ b/include/matador/query/query_part_visitor.hpp @@ -0,0 +1,67 @@ +#ifndef QUERY_QUERY_PART_VISITOR_HPP +#define QUERY_QUERY_PART_VISITOR_HPP + +namespace matador::query { + +namespace internal { +class query_select_part; +class query_from_part; +class query_join_part; +class query_on_part; +class query_where_part; +class query_group_by_part; +class query_order_by_part; +class query_order_by_asc_part; +class query_order_by_desc_part; +class query_offset_part; +class query_limit_part; +class query_insert_part; +class query_into_part; +class query_values_part; +class query_update_part; +class query_set_part; +class query_delete_part; +class query_delete_from_part; +class query_create_part; +class query_create_table_part; +class query_drop_part; +class query_drop_table_part; +} + +class query_part_visitor +{ +public: + virtual ~query_part_visitor() = default; + + virtual void visit(internal::query_select_part &select_part) = 0; + virtual void visit(internal::query_from_part &from_part) = 0; + virtual void visit(internal::query_join_part &join_part) = 0; + virtual void visit(internal::query_on_part &on_part) = 0; + virtual void visit(internal::query_where_part &where_part) = 0; + virtual void visit(internal::query_group_by_part &group_by_part) = 0; + virtual void visit(internal::query_order_by_part &order_by_part) = 0; + virtual void visit(internal::query_order_by_asc_part &order_by_asc_part) = 0; + virtual void visit(internal::query_order_by_desc_part &order_by_desc_part) = 0; + virtual void visit(internal::query_offset_part &offset_part) = 0; + virtual void visit(internal::query_limit_part &limit_part) = 0; + + virtual void visit(internal::query_insert_part &insert_part) = 0; + virtual void visit(internal::query_into_part &into_part) = 0; + virtual void visit(internal::query_values_part &values_part) = 0; + + virtual void visit(internal::query_update_part &update_part) = 0; + virtual void visit(internal::query_set_part &set_part) = 0; + + virtual void visit(internal::query_delete_part &delete_part) = 0; + virtual void visit(internal::query_delete_from_part &delete_from_part) = 0; + + virtual void visit(internal::query_create_part &create_part) = 0; + virtual void visit(internal::query_create_table_part &create_table_part) = 0; + + virtual void visit(internal::query_drop_part &drop_part) = 0; + virtual void visit(internal::query_drop_table_part &drop_table_part) = 0; +}; + +} + +#endif //QUERY_QUERY_PART_VISITOR_HPP diff --git a/include/matador/query/value_extractor.hpp b/include/matador/query/value_extractor.hpp new file mode 100644 index 0000000..4ac88d2 --- /dev/null +++ b/include/matador/query/value_extractor.hpp @@ -0,0 +1,94 @@ +#ifndef QUERY_VALUE_EXTRACTOR_HPP +#define QUERY_VALUE_EXTRACTOR_HPP + +#include "matador/query/fk_value_extractor.hpp" + +#include "matador/utils/attribute_writer.hpp" +#include "matador/utils/default_type_traits.hpp" + +#include + +namespace matador::query { + +class value_extractor final : public utils::attribute_writer +{ +private: + explicit value_extractor(std::vector &values); + +public: + template < class Type > + static std::vector extract(const Type &type) + { + std::vector values; + value_extractor gen(values); + access::process(gen, type); + return values; + } + + template + void on_primary_key(const char *, ValueType &x, std::enable_if_t && !std::is_same_v>* = nullptr) + { + utils::data_type_traits::bind_value(*this, 0, x); + } + void on_primary_key(const char *id, std::string &pk, size_t size); + void on_revision(const char *id, uint64_t &rev); + template < class Type > + void on_attribute(const char *, Type &x, const utils::field_attributes &/*attr*/ = utils::null_attributes) + { + utils::data_type_traits::bind_value(*this, 0, x); + } + void on_attribute(const char *id, char *x, const utils::field_attributes &/*attr*/ = utils::null_attributes); + void on_attribute(const char *id, std::string &x, const utils::field_attributes &/*attr*/ = utils::null_attributes); + + template class Pointer> + void on_belongs_to(const char * /*id*/, Pointer &x, const utils::foreign_attributes &/*attr*/) + { + values_.emplace_back(fk_value_extractor_.extract(*x)); + } + template class Pointer> + void on_has_one(const char * /*id*/, Pointer &x, const utils::foreign_attributes &/*attr*/) + { + values_.emplace_back(fk_value_extractor_.extract(*x)); + } + template + void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {} + template + void on_has_many_to_many(const char * /*id*/, + ContainerType &/*c*/, + const char * /*join_column*/, + const char * /*inverse_join_column*/, + const utils::foreign_attributes &/*attr*/) {} + template + void on_has_many_to_many(const char * /*id*/, + ContainerType &/*c*/, + const utils::foreign_attributes &/*attr*/) {} + +public: + void write_value(size_t pos, const int8_t &x) override; + void write_value(size_t pos, const int16_t &x) override; + void write_value(size_t pos, const int32_t &x) override; + void write_value(size_t pos, const int64_t &x) override; + void write_value(size_t pos, const uint8_t &x) override; + void write_value(size_t pos, const uint16_t &x) override; + void write_value(size_t pos, const uint32_t &x) override; + void write_value(size_t pos, const uint64_t &x) override; + void write_value(size_t pos, const bool &x) override; + void write_value(size_t pos, const float &x) override; + void write_value(size_t pos, const double &x) override; + void write_value(size_t pos, const time &x ) override; + void write_value(size_t pos, const date &x ) override; + void write_value(size_t pos, const char *x) override; + void write_value(size_t pos, const char *x, size_t size) override; + void write_value(size_t pos, const std::string &x) override; + void write_value(size_t pos, const std::string &x, size_t size) override; + void write_value(size_t pos, const utils::blob &x) override; + void write_value(size_t pos, const utils::value &x, size_t size) override; + +private: + detail::fk_value_extractor fk_value_extractor_; + std::vector &values_; +}; + +} + +#endif //QUERY_VALUE_EXTRACTOR_HPP diff --git a/include/matador/sql/abstract_sql_logger.hpp b/include/matador/sql/abstract_sql_logger.hpp new file mode 100644 index 0000000..41325e9 --- /dev/null +++ b/include/matador/sql/abstract_sql_logger.hpp @@ -0,0 +1,86 @@ +#ifndef MATADOR_BASIC_SQL_LOGGER_HPP +#define MATADOR_BASIC_SQL_LOGGER_HPP + +#include + +namespace matador::sql { + +/** + * @brief Base class for sql logging + * + * This class acts as a base class to + * implement a concrete logger for sql + * statements. + * + * It provides interfaces to handle + * the establishing and closing of + * a database connection as well as + * when a sql statement is about to + * execute or going to be prepared. + */ +class abstract_sql_logger +{ +public: + + virtual ~abstract_sql_logger() = default; + /** + * Is called when a connection to a database is + * going to be established + */ + virtual void on_connect() = 0; + + /** + * Is called when a connection is going to be closed + */ + virtual void on_close() = 0; + + /** + * Is called when a sql statement is going to + * be executed + * + * @param stmt SQL statement to be executed + */ + virtual void on_execute(const std::string &stmt) = 0; + + /** + * Is called when a sql statement is going to + * be prepared + * + * @param stmt SQL statement to be prepared + */ + virtual void on_prepare(const std::string &stmt) = 0; +}; + +/** + * Implements the basic_sql_logger to do no + * logging at all. + * This is used as the default logger for all + * connections and statements. + */ +class null_sql_logger final : public abstract_sql_logger +{ +public: + /** + * No logging on establishing a connection. + */ + void on_connect() override { } + + /** + * No logging on closing a connection. + */ + void on_close() override { } + + /** + * No logging on executing a statement. + */ + void on_execute(const std::string &) override { } + + /** + * No logging on preparing a statement. + */ + void on_prepare(const std::string &) override { } +}; + +} + +#endif //MATADOR_BASIC_SQL_LOGGER_HPP diff --git a/include/matador/sql/any_type.hpp b/include/matador/sql/any_type.hpp deleted file mode 100644 index 38119eb..0000000 --- a/include/matador/sql/any_type.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef QUERY_ANY_TYPE_HPP -#define QUERY_ANY_TYPE_HPP - -#include "matador/sql/placeholder.hpp" - -#include "matador/utils/types.hpp" - -#include -#include - -namespace matador::sql { - -using any_db_type = std::variant< - long long, - unsigned long long, - double, - bool, - const char*, - std::string, - utils::blob, - placeholder, - nullptr_t>; - -using any_type = std::variant< - char, short, int, long, long long, - unsigned char, unsigned short, unsigned int, unsigned long, unsigned long long, - float, double, - bool, - const char*, - std::string, - utils::blob, - placeholder, - nullptr_t>; - -} -#endif //QUERY_ANY_TYPE_HPP diff --git a/include/matador/sql/any_type_to_string_visitor.hpp b/include/matador/sql/any_type_to_string_visitor.hpp deleted file mode 100644 index 2d076d6..0000000 --- a/include/matador/sql/any_type_to_string_visitor.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef QUERY_ANY_TYPE_TO_STRING_VISITOR_HPP -#define QUERY_ANY_TYPE_TO_STRING_VISITOR_HPP - -#include "matador/utils/types.hpp" - -#include "matador/sql/placeholder.hpp" - -#include - -namespace matador::sql { - -class dialect; -class query_context; - -struct any_type_to_string_visitor -{ - explicit any_type_to_string_visitor(const dialect &d, query_context &query); - - void operator()(char &x) { to_string(x); } - void operator()(short &x) { to_string(x); } - void operator()(int &x) { to_string(x); } - void operator()(long &x) { to_string(x); } - void operator()(long long &x) { to_string(x); } - void operator()(unsigned char &x) { to_string(x); } - void operator()(unsigned short &x) { to_string(x); } - void operator()(unsigned int &x) { to_string(x); } - void operator()(unsigned long &x) { to_string(x); } - void operator()(unsigned long long &x) { to_string(x); } - void operator()(bool &x) { to_string(x); } - void operator()(float &x) { to_string(x); } - void operator()(double &x) { to_string(x); } - void operator()(const char *x) { to_string(x); } - void operator()(std::string &x) { to_string(x); } - void operator()(utils::blob &x) { to_string(x); } - void operator()(placeholder &x) { to_string(x); } - - template - void to_string(Type &val) - { - result = std::to_string(val); - } - void to_string(const char *val); - void to_string(std::string &val); - void to_string(utils::blob &val); - void to_string(placeholder &val); - - const dialect &d; - query_context &query; - std::string result; -}; - -} -#endif //QUERY_ANY_TYPE_TO_STRING_VISITOR_HPP diff --git a/include/matador/sql/any_type_to_visitor.hpp b/include/matador/sql/any_type_to_visitor.hpp deleted file mode 100644 index 925cd89..0000000 --- a/include/matador/sql/any_type_to_visitor.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef QUERY_ANY_TYPE_TO_VISITOR_HPP -#define QUERY_ANY_TYPE_TO_VISITOR_HPP - -#include "matador/sql/convert.hpp" - -#include - -namespace matador::sql { - -struct placeholder; - -template < typename Type > -struct any_type_to_visitor -{ - void operator()(char &x) { convert(result, x); } - void operator()(short &x) { convert(result, x); } - void operator()(int &x) { convert(result, x); } - void operator()(long &x) { convert(result, x); } - void operator()(long long &x) { convert(result, x); } - void operator()(unsigned char &x) { convert(result, x); } - void operator()(unsigned short &x) { convert(result, x); } - void operator()(unsigned int &x) { convert(result, x); } - void operator()(unsigned long &x) { convert(result, x); } - void operator()(unsigned long long &x) { convert(result, x); } - void operator()(bool &x) { convert(result, x); } - void operator()(float &x) { convert(result, x); } - void operator()(double &x) { convert(result, x); } - void operator()(const char *x) { convert(result, x); } - void operator()(std::string &x) { convert(result, x); } - void operator()(utils::blob &x) { convert(result, x); } - void operator()(placeholder &/*x*/) {} - - Type result{}; -}; - -} - -#endif //QUERY_ANY_TYPE_TO_VISITOR_HPP diff --git a/include/matador/sql/backend_provider.hpp b/include/matador/sql/backend_provider.hpp index af2fdeb..b024bc2 100644 --- a/include/matador/sql/backend_provider.hpp +++ b/include/matador/sql/backend_provider.hpp @@ -6,7 +6,6 @@ #include #include #include -#include namespace matador::sql { @@ -20,28 +19,31 @@ private: backend_provider(); public: + struct basic_backend_service { + virtual ~basic_backend_service() = default; + [[nodiscard]] virtual connection_impl* create(const connection_info&) = 0; + virtual void destroy(connection_impl*) = 0; + [[nodiscard]] virtual const sql::dialect* dialect() const = 0; + }; + static backend_provider& instance(); connection_impl* create_connection(const std::string &connection_type, const connection_info &info); void destroy_connection(const std::string &connection_type, connection_impl *c); const dialect& connection_dialect(const std::string &connection_type); -private: - struct basic_backend_context { - virtual ~basic_backend_context() = default; - [[nodiscard]] virtual connection_impl* create(const connection_info&) = 0; - virtual void destroy(connection_impl*) = 0; - [[nodiscard]] virtual const sql::dialect* dialect() const = 0; - }; + void register_backend(const std::string &connection_type, std::unique_ptr &&service); - class backend_context final : public basic_backend_context { +private: + + class backend_service final : public basic_backend_service { public: - explicit backend_context(const std::string &connection_type); - backend_context(const backend_context&) = delete; - backend_context& operator=(const backend_context&) = delete; - backend_context(backend_context&&) noexcept = default; - backend_context& operator=(backend_context&&) noexcept = default; - ~backend_context() override; + explicit backend_service(const std::string &connection_type); + backend_service(const backend_service&) = delete; + backend_service& operator=(const backend_service&) = delete; + backend_service(backend_service&&) noexcept = default; + backend_service& operator=(backend_service&&) noexcept = default; + ~backend_service() override; [[nodiscard]] connection_impl* create(const connection_info&) override; void destroy(connection_impl *conn) override; @@ -58,18 +60,8 @@ private: utils::library lib; }; - class noop_backend_context final : public basic_backend_context { - public: - connection_impl *create(const connection_info &info) override; - void destroy(connection_impl *impl) override; - [[nodiscard]] const sql::dialect *dialect() const override; - - private: - std::unordered_set> noop_connections_; - }; - private: - using backends_t = std::unordered_map>; + using backends_t = std::unordered_map>; backends_t backends_; }; } diff --git a/include/matador/sql/column.hpp b/include/matador/sql/column.hpp index 4df3d36..87a86a1 100644 --- a/include/matador/sql/column.hpp +++ b/include/matador/sql/column.hpp @@ -1,6 +1,8 @@ #ifndef QUERY_COLUMN_HPP #define QUERY_COLUMN_HPP +#include +#include #include namespace matador::sql { @@ -18,20 +20,21 @@ enum class sql_function_t { struct column { - column(const char *name); // NOLINT(*-explicit-constructor) - column(std::string name); // NOLINT(*-explicit-constructor) + column(const char *name, const std::string& as = ""); // NOLINT(*-explicit-constructor) + explicit column(std::string name, std::string as = ""); // NOLINT(*-explicit-constructor) column(sql_function_t func, std::string name); // NOLINT(*-explicit-constructor) - column(std::string table_name, std::string name, std::string as = ""); - column(std::string table_name, const char* name, std::string as = ""); - column(struct table &t, const char* name, std::string as = ""); + column(const struct table &t, std::string name, std::string as = ""); + column(const std::shared_ptr
&t, std::string name, std::string as = ""); [[nodiscard]] bool equals(const column &x) const; column& as(std::string a); [[nodiscard]] bool is_function() const; + [[nodiscard]] bool has_alias() const; - std::string table; + std::shared_ptr
table_; + using table_ref = std::reference_wrapper; std::string name; std::string alias; sql_function_t function_{sql_function_t::NONE}; diff --git a/include/matador/sql/column_definition.hpp b/include/matador/sql/column_definition.hpp index d489f58..657a4d5 100644 --- a/include/matador/sql/column_definition.hpp +++ b/include/matador/sql/column_definition.hpp @@ -1,13 +1,12 @@ #ifndef QUERY_COLUMN_DEFINITION_HPP #define QUERY_COLUMN_DEFINITION_HPP -#include "matador/sql/any_type.hpp" -#include "matador/sql/any_type_to_visitor.hpp" -#include "matador/sql/data_type_traits.hpp" - +#include "matador/utils/basic_type_converter.hpp" +#include "matador/utils/basic_types.hpp" +#include "matador/utils/default_type_traits.hpp" #include "matador/utils/field_attributes.hpp" +#include "matador/utils/value.hpp" -#include #include namespace matador::sql { @@ -27,42 +26,51 @@ public: column_definition& operator=(column_definition&&) noexcept = default; template - explicit column_definition(std::string name, utils::field_attributes attr) - : column_definition(std::move(name), data_type_traits::builtin_type(attr.size()), attr) + explicit column_definition(std::string name, const utils::field_attributes& attr) + : column_definition(std::move(name), utils::data_type_traits::type(attr.size()), attr) {} template - column_definition(std::string name, const Type &, utils::field_attributes attr, null_option null_opt) - : column_definition(std::move(name), data_type_traits::builtin_type(attr.size()), attr, null_opt) + column_definition(std::string name, const Type &, const utils::field_attributes& attr, null_option null_opt) + : column_definition(std::move(name), utils::data_type_traits::type(attr.size()), attr, null_opt) {} - column_definition(std::string name, data_type_t type, utils::field_attributes attr, null_option null_opt, size_t index = 0); + template + column_definition(std::string name, const char (&)[SIZE], const utils::field_attributes& attr, const null_option null_opt) + : column_definition(std::move(name), utils::data_type_traits::type(attr.size()), attr, null_opt) + {} + + column_definition(std::string name, utils::basic_type type, const utils::field_attributes&, null_option null_opt, size_t index = 0); template - column_definition(std::string name, std::string ref_table, std::string ref_column, utils::field_attributes attr, null_option null_opt) - : column_definition(std::move(name), data_type_traits::builtin_type(attr.size()), ref_table, ref_column, attr, null_opt) + column_definition(std::string name, std::string ref_table, std::string ref_column, const utils::field_attributes& attr, null_option null_opt) + : column_definition(std::move(name), utils::data_type_traits::type(attr.size()), ref_table, ref_column, attr, null_opt) {} - column_definition(std::string name, data_type_t type, size_t index, std::string ref_table, std::string ref_column, utils::field_attributes attr, null_option null_opt); + column_definition(std::string name, utils::basic_type type, size_t index, std::string ref_table, std::string ref_column, const utils::field_attributes& attr, null_option null_opt); [[nodiscard]] const std::string& name() const; + [[nodiscard]] std::string full_name() const; + [[nodiscard]] std::string table_name() const; [[nodiscard]] int index() const; [[nodiscard]] const utils::field_attributes& attributes() const; [[nodiscard]] bool is_nullable() const; - [[nodiscard]] data_type_t type() const; + [[nodiscard]] utils::basic_type type() const; [[nodiscard]] const std::string& ref_table() const; [[nodiscard]] const std::string& ref_column() const; + [[nodiscard]] bool is_foreign_reference() const; [[nodiscard]] bool is_integer() const; [[nodiscard]] bool is_floating_point() const; [[nodiscard]] bool is_bool() const; [[nodiscard]] bool is_string() const; [[nodiscard]] bool is_varchar() const; + [[nodiscard]] bool is_date() const; + [[nodiscard]] bool is_time() const; [[nodiscard]] bool is_blob() const; [[nodiscard]] bool is_null() const; - [[nodiscard]] bool is_unknown() const; - void type(data_type_t type); + void type(utils::basic_type type); template< typename Type > [[nodiscard]] bool is_type_of() const { @@ -74,35 +82,26 @@ public: template void set(const Type &value, const utils::field_attributes &attr = utils::null_attributes) { - type_ = data_type_traits::builtin_type(attr.size()); attributes_ = attr; value_ = value; } void set(const std::string &value, const utils::field_attributes &attr) { - type_ = data_type_traits::builtin_type(attr.size()); - attributes_ = attr; value_ = value; + attributes_ = attr; } void set(const char *value, const utils::field_attributes &attr) { - type_ = data_type_traits::builtin_type(attr.size()); + value_ = std::string(value); attributes_ = attr; - value_ = value; } template - Type as() const + std::optional as() const { - const Type* ptr= std::get_if(&value_); - if (ptr) { - return *ptr; - } - any_type_to_visitor visitor; - std::visit(visitor, const_cast(value_)); - return visitor.result; + return value_.as(); } friend std::ostream& operator<<(std::ostream &out, const column_definition &col); @@ -111,20 +110,20 @@ private: template void process(Operator &op) { - op.on_attribute(name_.c_str(), value_, type_, attributes_); + op.on_attribute(name_.c_str(), value_, attributes_); } - using data_type_index = std::vector; + using data_type_index = std::vector; private: static const data_type_index data_type_index_; std::string name_; + std::string table_; int index_{-1}; utils::field_attributes attributes_; null_option null_option_{null_option::NOT_NULL}; - data_type_t type_{data_type_t::type_unknown}; - any_type value_; + utils::value value_; std::string ref_table_; std::string ref_column_; }; @@ -132,15 +131,17 @@ private: /** * User defined literal to have a shortcut creating a column object * @param name Name of the column - * @param len Length of the column name + * @param type + * @param attr Length of the column name + * @param null_opt * @return A column object with given name */ -column_definition make_column(const std::string &name, data_type_t type, utils::field_attributes attr = utils::null_attributes, null_option null_opt = null_option::NOT_NULL); +column_definition make_column(const std::string &name, utils::basic_type type, utils::field_attributes attr = utils::null_attributes, null_option null_opt = null_option::NOT_NULL); template < typename Type > column_definition make_column(const std::string &name, utils::field_attributes attr = utils::null_attributes, null_option null_opt = null_option::NOT_NULL) { - return make_column(name, data_type_traits::builtin_type(0), attr, null_opt); + return make_column(name, utils::data_type_traits::type(0), attr, null_opt); } template <> column_definition make_column(const std::string &name, utils::field_attributes attr, null_option null_opt); @@ -157,13 +158,13 @@ column_definition make_pk_column(const std::string &name, size_t si template < typename Type > column_definition make_fk_column(const std::string &name, size_t size, const std::string &ref_table, const std::string &ref_column) { - return {name, data_type_traits::builtin_type(size), ref_table, ref_column, { size, utils::constraints::FOREIGN_KEY }}; + return {name, utils::data_type_traits::type(size), ref_table, ref_column, { size, utils::constraints::FOREIGN_KEY }}; } template < typename Type > [[maybe_unused]] column_definition make_fk_column(const std::string &name, const std::string &ref_table, const std::string &ref_column) { - return {name, data_type_traits::builtin_type(0), 0, ref_table, ref_column, { 0, utils::constraints::FOREIGN_KEY }, null_option::NOT_NULL}; + return {name, utils::data_type_traits::type(0), 0, ref_table, ref_column, { 0, utils::constraints::FOREIGN_KEY }, null_option::NOT_NULL}; } template <> diff --git a/include/matador/sql/column_definition_generator.hpp b/include/matador/sql/column_definition_generator.hpp deleted file mode 100644 index b026330..0000000 --- a/include/matador/sql/column_definition_generator.hpp +++ /dev/null @@ -1,133 +0,0 @@ -#ifndef QUERY_COLUMN_DEFINITION_GENERATOR_HPP -#define QUERY_COLUMN_DEFINITION_GENERATOR_HPP - -#include "matador/sql/column_definition.hpp" -#include "matador/sql/data_type_traits.hpp" - -#include "matador/utils/access.hpp" -#include "matador/utils/field_attributes.hpp" -#include "matador/utils/foreign_attributes.hpp" - -#include -#include - -namespace matador::sql { - -class schema; - -class fk_column_generator -{ -public: - fk_column_generator() = default; - - template - column_definition generate(const char *id, Type &x, const std::string &ref_table, const std::string &ref_column) - { - utils::access::process(*this, x); - return column_definition{id, type_, 0, ref_table, ref_column, {utils::constraints::FOREIGN_KEY }, null_option::NOT_NULL}; - } - - template - void on_primary_key(const char *, ValueType &/*pk*/, typename std::enable_if::value && !std::is_same::value>::type* = 0) - { - type_ = data_type_traits::builtin_type(0); - } - void on_primary_key(const char * /*id*/, std::string &/*pk*/, size_t size); - void on_revision(const char * /*id*/, unsigned long long &/*rev*/) {} - template < class Type > - void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} - void on_attribute(const char * /*id*/, char * /*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} - template - void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {} - template - void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {} - template - void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {} - template - void on_has_many_to_many(const char *id, ContainerType &c, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &/*attr*/) {} - template - void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {} - -private: - data_type_t type_{}; -}; - -class column_definition_generator -{ -private: - column_definition_generator(std::vector &columns, const schema &repo); - -public: - ~column_definition_generator() = default; - - template < class Type > - static std::vector generate(const schema &repo) - { - std::vector columns; - column_definition_generator gen(columns, repo); - Type obj; - matador::utils::access::process(gen, obj); - return std::move(columns); - } - - template < class V > - void on_primary_key(const char *, V &x, typename std::enable_if::value && !std::is_same::value>::type* = 0); - void on_primary_key(const char *id, std::string &pk, size_t size); - void on_revision(const char *id, unsigned long long &rev); - - template - void on_attribute(const char *id, Type &x, const utils::field_attributes &attr = utils::null_attributes); - - template - void on_attribute(const char *id, std::optional &x, const utils::field_attributes &attr = utils::null_attributes); - - template - void on_belongs_to(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/) - { - const auto [ref_table, ref_column] = determine_foreign_ref(std::type_index(typeid(typename Pointer::value_type))); - columns_.push_back(fk_column_generator_.generate(id, *x, ref_table, ref_column)); - } - template - void on_has_one(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/) - { - const auto [ref_table, ref_column] = determine_foreign_ref(std::type_index(typeid(typename Pointer::value_type))); - columns_.push_back(fk_column_generator_.generate(id, *x, ref_table, ref_column)); - } - template - void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {} - template - void on_has_many_to_many(const char *id, ContainerType &c, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &/*attr*/) {} - template - void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {} - -private: - std::pair determine_foreign_ref(const std::type_index &ti); - -private: - size_t index_ = 0; - std::vector &columns_; - const schema &repo_; - - fk_column_generator fk_column_generator_; -}; - -template -void column_definition_generator::on_primary_key(const char *id, V &x, typename std::enable_if::value && !std::is_same::value>::type*) -{ - on_attribute(id, x, { utils::constraints::PRIMARY_KEY }); -} - -template -void column_definition_generator::on_attribute(const char *id, Type &x, const utils::field_attributes &attr) -{ - columns_.emplace_back(id, x, attr, null_option::NOT_NULL); -} - -template -void column_definition_generator::on_attribute(const char *id, std::optional &x, const utils::field_attributes &attr) -{ - columns_.emplace_back(id, data_type_traits::builtin_type(attr.size()), attr, null_option::NULLABLE); -} - -} -#endif //QUERY_COLUMN_DEFINITION_GENERATOR_HPP diff --git a/include/matador/sql/column_generator.hpp b/include/matador/sql/column_generator.hpp deleted file mode 100644 index ec0e47b..0000000 --- a/include/matador/sql/column_generator.hpp +++ /dev/null @@ -1,128 +0,0 @@ -#ifndef QUERY_COLUMN_GENERATOR_HPP -#define QUERY_COLUMN_GENERATOR_HPP - -#include "matador/utils/access.hpp" -#include "matador/utils/field_attributes.hpp" -#include "matador/utils/foreign_attributes.hpp" - -#include "matador/sql/column.hpp" -#include "matador/sql/schema.hpp" - -#include -#include -#include - -namespace matador::sql { - -class column_generator -{ -private: - column_generator(std::vector &column_infos, - const sql::schema &ts, - const std::string &table_name, - bool force_lazy); - -public: - ~column_generator() = default; - - template < class Type > - static std::vector generate(const sql::schema &ts, bool force_lazy = false) - { - const auto info = ts.info(); - if (!info) { - return {}; - } - std::vector columns; - column_generator gen(columns, ts, info.value().name, force_lazy); - Type obj; - matador::utils::access::process(gen, obj); - return std::move(columns); - } - - template < class V > - void on_primary_key(const char *id, V &, typename std::enable_if::value && !std::is_same::value>::type* = 0) - { - push(id); - } - void on_primary_key(const char *id, std::string &, size_t); - void on_revision(const char *id, unsigned long long &/*rev*/); - - template - void on_attribute(const char *id, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes) - { - push(id); - } - - template - void on_belongs_to(const char *id, Pointer &, const utils::foreign_attributes &attr) - { - if (attr.fetch() == utils::fetch_type::LAZY || force_lazy_) { - push(id); - } else { - const auto info = table_schema_.info(); - if (!info) { - return; - } - table_name_stack_.push(info.value().name); - typename Pointer::value_type obj; - matador::utils::access::process(*this, obj); - table_name_stack_.pop(); - } - } - template - void on_has_one(const char *id, Pointer &, const utils::foreign_attributes &attr) - { - if (attr.fetch() == utils::fetch_type::LAZY || force_lazy_) { - push(id); - } else { - const auto info = table_schema_.info(); - if (!info) { - return; - } - table_name_stack_.push(info.value().name); - typename Pointer::value_type obj; - matador::utils::access::process(*this, obj); - table_name_stack_.pop(); - } - } - template - void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &attr) - { - if (attr.fetch() == utils::fetch_type::LAZY || force_lazy_) { - return; - } - const auto info = table_schema_.info(); - if (!info) { - return; - } - - table_name_stack_.push(info.value().name); - typename ContainerType::value_type::value_type obj; - matador::utils::access::process(*this, obj); - table_name_stack_.pop(); - } - - template - void on_has_many_to_many(const char *id, ContainerType &c, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &attr) - { - } - - template - void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &attr) - { - } - -private: - void push(const std::string &column_name); - -private: - std::stack table_name_stack_; - std::vector &column_infos_; - const sql::schema &table_schema_; - int column_index{0}; - bool force_lazy_{false}; -}; - -} - -#endif //QUERY_COLUMN_GENERATOR_HPP diff --git a/include/matador/sql/connection.hpp b/include/matador/sql/connection.hpp index d1d72ea..0878dcc 100644 --- a/include/matador/sql/connection.hpp +++ b/include/matador/sql/connection.hpp @@ -1,58 +1,153 @@ #ifndef QUERY_CONNECTION_HPP #define QUERY_CONNECTION_HPP +#include "matador/sql/abstract_sql_logger.hpp" +#include "matador/sql/column_definition.hpp" #include "matador/sql/connection_info.hpp" -#include "matador/sql/connection_impl.hpp" -#include "matador/sql/dialect.hpp" -#include "matador/sql/query.hpp" -#include "matador/sql/query_context.hpp" -#include "matador/sql/query_result.hpp" -#include "matador/sql/record.hpp" +#include "matador/sql/executor.hpp" #include "matador/sql/statement.hpp" -#include "matador/utils/logger.hpp" - #include namespace matador::sql { - class schema; +class connection_impl; -class connection -{ +/** + * @brief The connection class represents a connection to a database. + */ +class connection final : public executor { public: - explicit connection(connection_info info); - explicit connection(const std::string& dns); + /** + * @brief Creates a database connection from a connection info data. + * + * @param info The database connection info data + * @param sql_logger The logging handler + */ + explicit connection(connection_info info, + const std::shared_ptr &sql_logger = std::make_shared()); + /** + * @brief Creates a database connection from a connection string. + * + * @param dns The database connection string + * @param sql_logger The logging handler + */ + explicit connection(const std::string &dns, + const std::shared_ptr &sql_logger = std::make_shared()); + /** + * Copies a given connection + * + * @param x The connection to copy + */ connection(const connection &x); - connection& operator=(const connection &x); + /** + * Assigns from the given connection + * + * @param x The connection to assign + * @return The reference to the assigned connection + */ + connection &operator=(const connection &x); + /** + * Copy moves a given connection + * + * @param x The connection to copy move + */ connection(connection &&x) noexcept = default; - ~connection(); + /** + * Assigns moves from the given connection + * + * @param x The connection to assign move + * @return The reference to the assigned connection + */ + connection& operator=(connection &&x) noexcept; - void open(); - void close(); - [[nodiscard]] bool is_open() const; - [[nodiscard]] const connection_info& info() const; + ~connection() override; - [[nodiscard]] std::vector describe(const std::string &table_name) const; - [[nodiscard]] bool exists(const std::string &schema_name, const std::string &table_name) const; - [[nodiscard]] bool exists(const std::string &table_name) const; + /** + * @brief Opens the database connection for the given dns. + * + * Opens the database connection. If database connection + * couldn't be opened an exception is thrown. + */ + [[nodiscard]] utils::result open() const; + /** + * @brief Closes the database connection. + * + * Closes the database connection. + */ + [[nodiscard]] utils::result close() const; + /** + * @brief Returns true if database connection is open. + * + * Returns true if database connection is open + * + * @return True on open database connection. + */ + [[nodiscard]] utils::result is_open() const; - sql::query query(const sql::schema &schema) const; + /** + * Returns the connection info data of the + * current database connection. + * + * @return Returns the connection info data + */ + [[nodiscard]] const connection_info &info() const; + /** + * @brief Return the database type of the connection. + * + * Returns the database type of the connection which is + * currently one of + * - mssql + * - mysql + * - sqlite + * - postgres + * + * @return The database type string + */ + [[nodiscard]] std::string type() const; - query_result fetch(const query_context &q) const; - [[nodiscard]] std::unique_ptr fetch(const std::string &sql) const; - [[nodiscard]] size_t execute(const std::string &sql) const; + /** + * @brief Starts a transaction by calling the + * underlying database backends transaction begin + * statement. + */ + [[nodiscard]] utils::result begin() const; - statement prepare(query_context &&query) const; + /** + * @brief Commits a transaction by calling the + * underlying database backends transaction commit + * statement. + */ + [[nodiscard]] utils::result commit() const; - const class dialect& dialect() const; + /** + * @brief Rollback a transaction by calling the + * underlying database backends transaction rollback/abort + * statement. + */ + [[nodiscard]] utils::result rollback() const; + + [[nodiscard]] utils::result, utils::error> describe(const std::string &table_name) const; + [[nodiscard]] utils::result exists(const std::string &schema_name, const std::string &table_name) const; + [[nodiscard]] utils::result exists(const std::string &table_name) const; + + [[nodiscard]] utils::result execute(const std::string &sql) const; + + [[nodiscard]] utils::result, utils::error> fetch(const query_context &ctx) const override; + [[nodiscard]] utils::result execute(const query_context &ctx) const override; + [[nodiscard]] utils::result prepare(const query_context &ctx) const override; + [[nodiscard]] std::string str( const query_context& ctx ) const override; + + [[nodiscard]] const class dialect &dialect() const override; private: + friend class fetchable_query; + friend class session; + connection_info connection_info_; std::unique_ptr connection_; - utils::logger logger_; - const class dialect &dialect_; + std::shared_ptr logger_ = std::make_shared(); }; - } + #endif //QUERY_CONNECTION_HPP diff --git a/include/matador/sql/connection_impl.hpp b/include/matador/sql/connection_impl.hpp deleted file mode 100644 index f633641..0000000 --- a/include/matador/sql/connection_impl.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef QUERY_CONNECTION_IMPL_HPP -#define QUERY_CONNECTION_IMPL_HPP - -#include "matador/sql/connection_info.hpp" -#include "matador/sql/query_result_impl.hpp" -#include "matador/sql/query_context.hpp" -#include "matador/sql/record.hpp" -#include "matador/sql/statement_impl.hpp" - -#include - -namespace matador::sql { - -class query_result_impl; - -class connection_impl -{ -public: - virtual ~connection_impl() = default; - - virtual void open() = 0; - virtual void close() = 0; - virtual bool is_open() = 0; - - virtual size_t execute(const std::string &stmt) = 0; - virtual std::unique_ptr fetch(const std::string &stmt) = 0; - virtual std::unique_ptr prepare(query_context context) = 0; - - virtual std::vector describe(const std::string &table) = 0; - virtual bool exists(const std::string &schema_name, const std::string &table_name) = 0; - -protected: - explicit connection_impl(const connection_info &info); - - [[nodiscard]] const connection_info &info() const; - -private: - const connection_info & info_; -}; - -} -#endif //QUERY_CONNECTION_IMPL_HPP diff --git a/include/matador/sql/connection_info.hpp b/include/matador/sql/connection_info.hpp index 78a6660..68192e1 100644 --- a/include/matador/sql/connection_info.hpp +++ b/include/matador/sql/connection_info.hpp @@ -1,23 +1,66 @@ #ifndef QUERY_CONNECTION_INFO_HPP #define QUERY_CONNECTION_INFO_HPP +#include "matador/utils/access.hpp" + #include namespace matador::sql { -struct connection_info -{ - std::string type; - std::string user; - std::string password; - std::string hostname; - unsigned short port{}; - std::string database; - std::string driver; +/** + * This class contains all information + * about a database connection consisting + * of + * - database type + * - username + * - password + * - hostname + * - port + * - database name + * - driver + */ +struct connection_info { + std::string type{}; /**< Type of the database i.e. sqlite or mysql. */ + std::string user{}; /**< Username to login. */ + std::string password{}; /**< Password for login. */ + std::string hostname{}; /**< Hostname of the database. */ + unsigned short port{}; /**< Port of the database server. */ + std::string database{}; /**< Name of the database to use */ + std::string driver{}; /**< Driver to use. This is used by th mssql/odbc backends. */ - static connection_info parse(const std::string &info, unsigned short default_port = 0, const std::string &default_driver = ""); + template < class Operator > + void process(Operator &op) + { + namespace field = matador::access; + field::attribute(op, "type", type); + field::attribute(op, "user", user); + field::attribute(op, "password", password); + field::attribute(op, "hostname", hostname); + field::attribute(op, "port", port); + field::attribute(op, "database", database); + field::attribute(op, "driver", driver); + } + + /** + * This parses database uri into a connection_info object. + * + * @param info The database uri + * @param default_port The default port to use + * @param default_driver The default driver to use + * @return A connection_info object + */ + static connection_info parse(const std::string &info, + unsigned short default_port = 0, + const std::string &default_driver = ""); + + /** + * Convert a connection_info object + * into a database uri. + * + * @param ci The connection_info object to convert + * @return The corresponding database uri + */ static std::string to_string(const connection_info &ci); }; - } #endif //QUERY_CONNECTION_INFO_HPP diff --git a/include/matador/sql/connection_pool.hpp b/include/matador/sql/connection_pool.hpp deleted file mode 100644 index 8afca66..0000000 --- a/include/matador/sql/connection_pool.hpp +++ /dev/null @@ -1,188 +0,0 @@ -#ifndef QUERY_CONNECTION_POOL_HPP -#define QUERY_CONNECTION_POOL_HPP - -#include "matador/sql/connection_info.hpp" - -#include -#include -#include -#include -#include -#include -#include - -namespace matador::sql { - -template < class Connection > -class connection_pool; - -template < class Connection > -using IdConnection = std::pair; - -template < class Connection > -class connection_ptr -{ -public: - connection_ptr(IdConnection *c, connection_pool *pool) - : connection_(c), pool_(pool) {} - ~connection_ptr(); - connection_ptr(const connection_ptr &) = delete; - connection_ptr& operator=(const connection_ptr &) = delete; - connection_ptr(connection_ptr &&x) noexcept - : connection_(x.connection_) - , pool_(x.pool_) - { - x.connection_ = nullptr; - x.pool_ = nullptr; - } - connection_ptr& operator=(connection_ptr &&x) noexcept - { - if (this == &x) { - return *this; - } - - std::swap(connection_, x.connection_); - std::swap(pool_, x.pool_); - - return *this; - } - - Connection* operator->() { return &connection_->second; } - Connection& operator*() { return connection_->second; } - - [[nodiscard]] std::optional id() const - { - if (connection_) { - return connection_->first; - } else { - return std::nullopt; - } - } - [[nodiscard]] bool valid() const { return connection_ != nullptr; } - -private: - friend class connection_pool; - - IdConnection *connection_{}; - connection_pool *pool_{}; -}; - -template < class Connection > -class connection_pool -{ -public: - using connection_pointer = connection_ptr; - -public: - connection_pool(const std::string &dns, size_t count) - : info_(connection_info::parse(dns)) { - connection_repo_.reserve(count); - while (count) { - connection_repo_.emplace_back(count, info_); - auto &conn = connection_repo_.back(); - idle_connections_.emplace(conn.first, &conn); - conn.second.open(); - --count; - } - } - - connection_pointer acquire() { - std::unique_lock lock(mutex_); - while (idle_connections_.empty()) { - cv.wait(lock); - } - - return get_next_connection(); - } - - connection_pointer try_acquire() { - std::unique_lock lock(mutex_); - if (idle_connections_.empty()) { - return {nullptr, this}; - } - - return get_next_connection(); - } - - connection_pointer acquire(size_t id) { - using namespace std::chrono_literals; - pointer next_connection{nullptr}; - auto try_count{0}; - std::unique_lock lock(mutex_); - - do { - if (auto it = idle_connections_.find(id); it != idle_connections_.end()) { - next_connection = it->second; - auto node = idle_connections_.extract(it); - inuse_connections_.insert(std::move(node)); - } else { - lock.unlock(); - std::this_thread::sleep_for(100ms); - lock.lock(); - } - } while(try_count++ < 5); - - return {next_connection, this}; - } - - void release(IdConnection *c) { - if (c == nullptr) { - return; - } - std::unique_lock lock(mutex_); - if (auto it = inuse_connections_.find(c->first); it != inuse_connections_.end()) { - auto node = inuse_connections_.extract(it); - idle_connections_.insert(std::move(node)); - } - } - - void release(connection_ptr &c) { - release(c.connection_); - c.connection_ = nullptr; - } - - std::size_t size() const { return connection_repo_.size(); } - std::size_t idle() const { - std::lock_guard guard(mutex_); - return idle_connections_.size(); - } - std::size_t inuse() const { - std::lock_guard guard(mutex_); - return inuse_connections_.size(); - } - - const connection_info &info() const { - return info_; - } - -private: - connection_pointer get_next_connection() { - pointer next_connection{nullptr}; - for (auto &item : idle_connections_) { - next_connection = item.second; - auto node = idle_connections_.extract(item.first); - inuse_connections_.insert(std::move(node)); - break; - } - return {next_connection, this}; - } -private: - mutable std::mutex mutex_; - std::condition_variable cv; - std::vector> connection_repo_; - using pointer = IdConnection*; - using connection_map = std::unordered_map; - connection_map inuse_connections_; - connection_map idle_connections_; - - const connection_info info_; -}; - -template -connection_ptr::~connection_ptr() { - pool_->release(connection_); -} - -} - -#endif //QUERY_CONNECTION_POOL_HPP diff --git a/include/matador/sql/convert.hpp b/include/matador/sql/convert.hpp deleted file mode 100644 index edb9b8e..0000000 --- a/include/matador/sql/convert.hpp +++ /dev/null @@ -1,124 +0,0 @@ -#ifndef QUERY_CONVERT_HPP -#define QUERY_CONVERT_HPP - -#include "matador/utils/types.hpp" - -#include -#include -#include -#include -#include - -namespace matador::sql { - -template < typename DestType, typename SourceType > -void convert(DestType &dest, SourceType source, typename std::enable_if::value>::type* = nullptr) -{ - dest = source; -} - -template < typename DestType, typename SourceType > -void convert(DestType &dest, SourceType source, typename std::enable_if::value && std::is_arithmetic::value && !std::is_same::value>::type* = nullptr) -{ - dest = static_cast(source); -} - -template < typename DestType, typename SourceType > -void convert(DestType &dest, SourceType source, typename std::enable_if::value && std::is_arithmetic::value && !std::is_same::value>::type* = nullptr) -{ - dest = static_cast(source); -} - -void convert(std::string &dest, bool source); - -template < typename SourceType > -void convert(std::string &dest, SourceType source, typename std::enable_if::value && !std::is_same::value>::type* = nullptr) -{ - std::array buffer{}; - auto [ptr, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), source, 10); - if (ec == std::errc{}) { - dest.assign(buffer.data(), ptr); - } else { - throw std::logic_error("couldn't convert value to std::string"); - } -} - -template < typename SourceType > -void convert(std::string &dest, SourceType source, typename std::enable_if::value>::type* = nullptr) -{ - std::array buffer{}; - auto [ptr, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), source); - if (ec == std::errc{}) { - dest.assign(buffer.data(), ptr); - } else { - throw std::logic_error("couldn't convert value to std::string"); - } -} - -template < typename SourceType > -void convert(utils::blob &dest, SourceType source, typename std::enable_if::value>::type* = nullptr) -{ - throw std::logic_error("couldn't convert value to matador::utils::blob"); -} - -void convert(std::string &dest, const char* source); - -unsigned long long to_unsigned_long_long(const char *source); - -template < typename DestType > -void convert(DestType &dest, const std::string &source, typename std::enable_if::value && std::is_unsigned::value>::type* = nullptr) -{ - dest = to_unsigned_long_long(source.c_str()); -} - -template < typename DestType > -void convert(DestType &dest, const char *source, typename std::enable_if::value && std::is_unsigned::value>::type* = nullptr) -{ - dest = to_unsigned_long_long(source); -} - -long long to_long_long(const char *source); - -template < typename DestType > -void convert(DestType &dest, const std::string &source, typename std::enable_if::value && std::is_signed::value>::type* = nullptr) -{ - dest = to_long_long(source.c_str()); -} - -template < typename DestType > -void convert(DestType &dest, const char *source, typename std::enable_if::value && std::is_signed::value>::type* = nullptr) -{ - dest = to_long_long(source); -} - -long double to_double(const char *source); - -template < typename DestType > -void convert(DestType &dest, const std::string &source, typename std::enable_if::value>::type* = nullptr) -{ - dest = to_double(source.c_str()); -} - -template < typename DestType > -void convert(DestType &dest, const char *source, typename std::enable_if::value>::type* = nullptr) -{ - dest = to_double(source); -} - -template < typename DestType > -void convert(DestType &dest, bool source, typename std::enable_if::value>::type* = nullptr) -{ - dest = static_cast(source); -} - -template < typename DestType > -void convert(DestType &dest, const utils::blob &data) -{ - throw std::logic_error("couldn't convert matador::utils::blob into destination type"); -} - -void convert(utils::blob &dest, const utils::blob &data); - -} - -#endif //QUERY_CONVERT_HPP diff --git a/include/matador/sql/data_type_traits.hpp b/include/matador/sql/data_type_traits.hpp deleted file mode 100644 index 6b5a2f1..0000000 --- a/include/matador/sql/data_type_traits.hpp +++ /dev/null @@ -1,257 +0,0 @@ -#ifndef QUERY_DATA_TYPE_TRAITS_HPP -#define QUERY_DATA_TYPE_TRAITS_HPP - -#include "matador/sql/any_type.hpp" - -#include "matador/utils/types.hpp" - -#include -#include - -namespace matador::sql { - -class query_result_reader; -class parameter_binder; -class result_parameter_binder; - -/** - * @brief Enumeration type of all supported builtin data types - */ -enum class data_type_t : uint8_t { - type_char = 0, /*!< Data type char */ - type_short, /*!< Data type short */ - type_int, /*!< Data type int */ - type_long, /*!< Data type long */ - type_long_long, /*!< Data type long long */ - type_unsigned_char, /*!< Data type unsigned char */ - type_unsigned_short, /*!< Data type unsigned short */ - type_unsigned_int, /*!< Data type unsigned int */ - type_unsigned_long, /*!< Data type unsigned long */ - type_unsigned_long_long, /*!< Data type unsigned long long */ - type_float, /*!< Data type float */ - type_double, /*!< Data type double */ - type_bool, /*!< Data type bool */ - type_char_pointer, /*!< Data type character pointer */ - type_varchar, /*!< Data type varchar */ - type_text, /*!< Data type text */ - type_date, /*!< Data type date */ - type_time, /*!< Data type time */ - type_blob, /*!< Data type blob */ - type_null, /*!< Data type null */ - type_unknown /*!< Data type unknown */ -}; - -/** - * @tparam T The type of the traits - * @brief Type traits for database types - * - * This class is used to determine and - * provide the correct size information - * for a data type - */ -template < class Type, class Enable = void > -struct data_type_traits; - -/// @cond MATADOR_DEV -template <> struct data_type_traits -{ - inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_null; } - static void read_value(query_result_reader &reader, const char *id, size_t index, nullptr_t &/*value*/); - static void bind_value(parameter_binder &binder, size_t index, nullptr_t &/*value*/); - static void bind_result_value(result_parameter_binder &binder, size_t index, nullptr_t &/*value*/); - inline static any_type create_value(const char &value) { return value; } -}; - -template <> struct data_type_traits -{ - inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_char; } - static void read_value(query_result_reader &reader, const char *id, size_t index, char &value); - static void bind_value(parameter_binder &binder, size_t index, char &value); - static void bind_result_value(result_parameter_binder &binder, size_t index, char &value); - inline static any_type create_value(const char &value) { return value; } -}; - -template <> struct data_type_traits -{ - inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_short; } - static void read_value(query_result_reader &reader, const char *id, size_t index, short &value); - static void bind_value(parameter_binder &binder, size_t index, short &value); - static void bind_result_value(result_parameter_binder &binder, size_t index, short &value); - inline static any_type create_value(const short &value) { return value; } -}; - -template <> struct data_type_traits -{ - inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_int; } - static void read_value(query_result_reader &reader, const char *id, size_t index, int &value); - static void bind_value(parameter_binder &binder, size_t index, int &value); - static void bind_result_value(result_parameter_binder &binder, size_t index, int &value); - inline static any_type create_value(const int &value) { return value; } -}; - -template <> struct data_type_traits -{ - inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_long; } - static void read_value(query_result_reader &reader, const char *id, size_t index, long &value); - static void bind_value(parameter_binder &binder, size_t index, long &value); - static void bind_result_value(result_parameter_binder &binder, size_t index, long &value); - inline static any_type create_value(const long &value) { return value; } -}; - -template <> struct data_type_traits -{ - inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_long_long; } - static void read_value(query_result_reader &reader, const char *id, size_t index, long long &value); - static void bind_value(parameter_binder &binder, size_t index, long long &value); - static void bind_result_value(result_parameter_binder &binder, size_t index, long long &value); - inline static any_type create_value(const long long &value) { return value; } -}; - -template <> struct data_type_traits -{ - inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_unsigned_char; } - static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned char &value); - static void bind_value(parameter_binder &binder, size_t index, unsigned char &value); - static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned char &value); - inline static any_type create_value(const unsigned char &value) { return value; } -}; - -template <> struct data_type_traits -{ - inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_unsigned_short; } - static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned short &value); - static void bind_value(parameter_binder &binder, size_t index, unsigned short &value); - static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned short &value); - inline static any_type create_value(const unsigned short &value) { return value; } -}; - -template <> struct data_type_traits -{ - inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_unsigned_int; } - static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned int &value); - static void bind_value(parameter_binder &binder, size_t index, unsigned int &value); - static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned int &value); - inline static any_type create_value(const unsigned int &value) { return value; } -}; - -template <> struct data_type_traits -{ - inline static data_type_t builtin_type(std::size_t /*size*/ = 0) { return data_type_t::type_unsigned_long; } - static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned long &value); - static void bind_value(parameter_binder &binder, size_t index, unsigned long &value); - static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned long &value); - inline static any_type create_value(const unsigned long &value) { return value; } -}; - -template <> struct data_type_traits -{ - inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_unsigned_long_long; } - static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned long long &value); - static void bind_value(parameter_binder &binder, size_t index, unsigned long long &value); - static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned long long &value); - inline static any_type create_value(const unsigned long long &value) { return value; } -}; - -template <> struct data_type_traits -{ - inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_bool; } - static void read_value(query_result_reader &reader, const char *id, size_t index, bool &value); - static void bind_value(parameter_binder &binder, size_t index, bool &value); - static void bind_result_value(result_parameter_binder &binder, size_t index, bool &value); - inline static any_type create_value(const bool &value) { return value; } -}; - -template <> struct data_type_traits -{ - inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_float; } - static void read_value(query_result_reader &reader, const char *id, size_t index, float &value); - static void bind_result_value(result_parameter_binder &binder, size_t index, float &value); - static void bind_value(parameter_binder &binder, size_t index, float &value); - inline static any_type create_value(const float &value) { return value; } -}; - -template <> struct data_type_traits -{ - inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_double; } - static void read_value(query_result_reader &reader, const char *id, size_t index, double &value); - static void bind_result_value(result_parameter_binder &binder, size_t index, double &value); - static void bind_value(parameter_binder &binder, size_t index, double &value); - inline static any_type create_value(const double &value) { return value; } -}; - -template <> struct data_type_traits -{ - inline static data_type_t builtin_type(std::size_t size) { return size == 0 ? data_type_t::type_text : data_type_t::type_char_pointer; } - static void read_value(query_result_reader &reader, const char *id, size_t index, const char* value, size_t size); - static void bind_value(parameter_binder &binder, size_t index, const char *value, size_t size = 0); - static void bind_result_value(result_parameter_binder &binder, size_t index, const char *value, size_t size = 0); - inline static any_type create_value(const char *value) { return value; } -}; - -template <> struct data_type_traits -{ - inline static data_type_t builtin_type(std::size_t size) { return size == 0 ? data_type_t::type_text : data_type_t::type_varchar; } - static void read_value(query_result_reader &reader, const char *id, size_t index, char *value, size_t size); - static void bind_value(parameter_binder &binder, size_t index, char *value, size_t size = 0); - static void bind_result_value(result_parameter_binder &binder, size_t index, char *value, size_t size = 0); - inline static any_type create_value(const char *value) { return value; } -}; - -template <> struct data_type_traits -{ - inline static data_type_t builtin_type(std::size_t size) { return size == 0 ? data_type_t::type_text : data_type_t::type_varchar; } - static void read_value(query_result_reader &reader, const char *id, size_t index, std::string &value, size_t size); - static void bind_value(parameter_binder &binder, size_t index, std::string &value, size_t size = 0); - static void bind_result_value(result_parameter_binder &binder, size_t index, std::string &value, size_t size = 0); - inline static any_type create_value(const std::string &value) { return value; } -}; - -template <> struct data_type_traits -{ - inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_blob; } - static void read_value(query_result_reader &reader, const char *id, size_t index, utils::blob &value); - static void bind_value(parameter_binder &binder, size_t index, utils::blob &value); - static void bind_result_value(result_parameter_binder &binder, size_t index, utils::blob &value); - inline static any_type create_value(const utils::blob &value) { return value; } -}; - -//template <> struct data_type_traits -//{ -// inline static database_type_t type(std::size_t /*size*/) { return database_type_t::type_date; } -// inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_date; } -// inline static unsigned long size() { return 255; } -// inline static const char* name() { return "matador::date"; } -//}; -// -//template <> struct data_type_traits -//{ -// inline static database_type_t type(std::size_t /*size*/) { return database_type_t::type_time; } -// inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_time; } -// inline static unsigned long size() { return 255; } -// inline static const char* name() { return "matador::time"; } -//}; - -template < typename EnumType > -struct data_type_traits>> -{ - inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_int; } - static void read_value(query_result_reader &reader, const char *id, size_t index, EnumType &value) - { - data_type_traits::read_value(reader, id, index, reinterpret_cast(value)); - } - static void bind_value(parameter_binder &binder, size_t index, EnumType &value) - { - data_type_traits::bind_value(binder, index, static_cast(value)); - } - static void bind_result_value(result_parameter_binder &binder, size_t index, EnumType &value) - { - data_type_traits::bind_result_value(binder, index, static_cast(value)); - } - static any_type create_value(const EnumType &value) { - return static_cast(value); - } -}; -/// @endcond - -} -#endif //QUERY_DATA_TYPE_TRAITS_HPP diff --git a/include/matador/sql/dialect.hpp b/include/matador/sql/dialect.hpp index 34d92bf..df158a7 100644 --- a/include/matador/sql/dialect.hpp +++ b/include/matador/sql/dialect.hpp @@ -2,9 +2,12 @@ #define QUERY_DIALECT_HPP #include "matador/sql/column.hpp" -#include "matador/sql/data_type_traits.hpp" +#include "matador/sql/dialect_token.hpp" + +#include "matador/utils/basic_types.hpp" +#include "matador/utils/types.hpp" +#include "matador/utils/string.hpp" -#include #include #include #include @@ -12,54 +15,11 @@ namespace matador::sql { +class connection_impl; + class dialect final { public: - enum class token_t : uint8_t - { - CREATE = 0, - DROP, - REMOVE, - INSERT, - UPDATE, - SELECT, - TABLE, - VALUES, - INSERT_VALUES, - COLUMNS, - COLUMN, - FROM, - JOIN, - ON, - INTO, - WHERE, - WHERE_CLAUSE, - AND, - OR, - LIKE, - ORDER_BY, - GROUP_BY, - ASC, - DESC, - LIMIT, - AS, - OFFSET, - DISTINCT, - SET, - UPDATE_VALUES, - NOT_NULL, - PRIMARY_KEY, - BEGIN, - COMMIT, - ROLLBACK, - START_QUOTE, - END_QUOTE, - STRING_QUOTE, - BEGIN_BINARY_DATA, - END_BINARY_DATA, - NONE - }; - /** * Holding enums concerning escaping identifiers */ @@ -68,26 +28,32 @@ public: ESCAPE_CLOSING_BRACKET /**< The escape quotes differ; escape the closing one */ }; - using token_to_string_map = std::unordered_map; - using data_type_to_string_map = std::unordered_map; + using token_to_string_map = std::unordered_map; + using data_type_to_string_map = std::unordered_map; using sql_func_to_string_map = std::unordered_map; using next_placeholder_func = std::function; + using to_escaped_string_func = std::function; public: - [[nodiscard]] const std::string& token_at(token_t token) const; - [[nodiscard]] const std::string& data_type_at(data_type_t type) const; + [[nodiscard]] const std::string& token_at(dialect_token token) const; + [[nodiscard]] const std::string& data_type_at(utils::basic_type type) const; /** - * Prepare sql dialect identifier for execution - * and escape quotes and quote the identifier - * string - * - * @param str The identifier string to be prepared - * @return The prepared string - */ + * Prepare sql dialect identifier for execution + * and escape quotes and quote the identifier + * string + * + * @param col The identifier string to be prepared + * @return The prepared string + */ [[nodiscard]] std::string prepare_identifier(const column &col) const; [[nodiscard]] std::string prepare_identifier_string(const std::string &col) const; + [[nodiscard]] std::string prepare_condition(const column &col) const; + + [[nodiscard]] const std::string& to_string(bool val) const; + + void bool_strings(const std::string &true_string, const std::string &false_string); /** * Prepare string literal @@ -123,7 +89,17 @@ public: * * @return How the identifier quotes should be escaped */ - [[nodiscard]] virtual escape_identifier_t identifier_escape_type() const; + [[nodiscard]] escape_identifier_t identifier_escape_type() const; + + /** + * Sets the identifier escape type. Possibilities are + * opening and closing escape characters are the same + * (ESCAPE_BOTH_SAME) or using a special closing + * escape character (ESCAPE_CLOSING_BRACKET) + * + * @param escape_identifier Identifier escape type + */ + void identifier_escape_type(escape_identifier_t escape_identifier); /** * Generates a next placeholder string. default is @@ -133,6 +109,8 @@ public: */ [[nodiscard]] std::string next_placeholder(const std::vector &bind_vars) const; + [[nodiscard]] std::string to_escaped_string(const utils::blob &value, const connection_impl *conn = nullptr) const; + /** * Returns the default schema name. * @@ -147,73 +125,73 @@ private: friend class dialect_builder; next_placeholder_func placeholder_func_ = [](size_t) { return "?"; }; + // to_escaped_string_func to_escaped_string_func_ = [](const utils::blob &val) { return utils::to_string(val); }; + + escape_identifier_t identifier_escape_type_ = escape_identifier_t::ESCAPE_BOTH_SAME; std::string default_schema_name_; + // std::unique_ptr compiler_; token_to_string_map tokens_ { - {token_t::CREATE, "CREATE"}, - {token_t::DROP, "DROP"}, - {token_t::REMOVE, "DELETE"}, - {token_t::INSERT, "INSERT"}, - {token_t::TABLE, "TABLE"}, - {token_t::INTO, "INTO"}, - {token_t::VALUES, "VALUES"}, - {token_t::UPDATE, "UPDATE"}, - {token_t::SELECT, "SELECT"}, - {token_t::COLUMNS, "COLUMNS"}, - {token_t::COLUMN, "COLUMN"}, - {token_t::FROM, "FROM"}, - {token_t::JOIN, "INNER JOIN"}, - {token_t::ON, "ON"}, - {token_t::WHERE, "WHERE"}, - {token_t::AND, "AND"}, - {token_t::OR, "OR"}, - {token_t::LIKE, "LIKE"}, - {token_t::ORDER_BY, "ORDER BY"}, - {token_t::GROUP_BY, "GROUP BY"}, - {token_t::ASC, "ASC"}, - {token_t::DESC, "DESC"}, - {token_t::OFFSET, "OFFSET"}, - {token_t::LIMIT, "LIMIT"}, - {token_t::AS, "AS"}, - {token_t::OFFSET, "OFFSET"}, - {token_t::DISTINCT, "DISTINCT"}, - {token_t::SET, "SET"}, - {token_t::NOT_NULL, "NOT NULL"}, - {token_t::PRIMARY_KEY, "PRIMARY KEY"}, - {token_t::BEGIN, "BEGIN TRANSACTION"}, - {token_t::COMMIT, "COMMIT TRANSACTION"}, - {token_t::ROLLBACK, "ROLLBACK TRANSACTION"}, - {token_t::START_QUOTE, "\""}, - {token_t::END_QUOTE, "\""}, - {token_t::STRING_QUOTE, "'"}, - {token_t::BEGIN_BINARY_DATA, "X'"}, - {token_t::END_BINARY_DATA, "'"}, - {token_t::NONE, ""} + {dialect_token::CREATE, "CREATE"}, + {dialect_token::DROP, "DROP"}, + {dialect_token::REMOVE, "DELETE"}, + {dialect_token::INSERT, "INSERT"}, + {dialect_token::TABLE, "TABLE"}, + {dialect_token::INTO, "INTO"}, + {dialect_token::VALUES, "VALUES"}, + {dialect_token::UPDATE, "UPDATE"}, + {dialect_token::SELECT, "SELECT"}, + {dialect_token::COLUMNS, "COLUMNS"}, + {dialect_token::COLUMN, "COLUMN"}, + {dialect_token::FROM, "FROM"}, + {dialect_token::JOIN, "LEFT JOIN"}, + {dialect_token::ON, "ON"}, + {dialect_token::WHERE, "WHERE"}, + {dialect_token::AND, "AND"}, + {dialect_token::OR, "OR"}, + {dialect_token::LIKE, "LIKE"}, + {dialect_token::ORDER_BY, "ORDER BY"}, + {dialect_token::GROUP_BY, "GROUP BY"}, + {dialect_token::ASC, "ASC"}, + {dialect_token::DESC, "DESC"}, + {dialect_token::OFFSET, "OFFSET"}, + {dialect_token::LIMIT, "LIMIT"}, + {dialect_token::AS, "AS"}, + {dialect_token::OFFSET, "OFFSET"}, + {dialect_token::DISTINCT, "DISTINCT"}, + {dialect_token::SET, "SET"}, + {dialect_token::NOT_NULL, "NOT NULL"}, + {dialect_token::PRIMARY_KEY, "PRIMARY KEY"}, + {dialect_token::BEGIN, "BEGIN TRANSACTION"}, + {dialect_token::COMMIT, "COMMIT TRANSACTION"}, + {dialect_token::ROLLBACK, "ROLLBACK TRANSACTION"}, + {dialect_token::START_QUOTE, "\""}, + {dialect_token::END_QUOTE, "\""}, + {dialect_token::STRING_QUOTE, "'"}, + {dialect_token::BEGIN_BINARY_DATA, "X'"}, + {dialect_token::END_BINARY_DATA, "'"}, + {dialect_token::NONE, ""} }; data_type_to_string_map data_types_ { - {data_type_t::type_char, "TINYINT"}, - {data_type_t::type_short, "SMALLINT"}, - {data_type_t::type_int, "INTEGER"}, - {data_type_t::type_long, "BIGINT"}, - {data_type_t::type_long_long, "BIGINT"}, - {data_type_t::type_unsigned_char, "TINYINT"}, - {data_type_t::type_unsigned_short, "INTEGER"}, - {data_type_t::type_unsigned_int, "BIGINT"}, - {data_type_t::type_unsigned_long, "BIGINT"}, - {data_type_t::type_unsigned_long_long, "BIGINT"}, - {data_type_t::type_float, "FLOAT"}, - {data_type_t::type_double, "DOUBLE"}, - {data_type_t::type_bool, "BOOLEAN"}, - {data_type_t::type_char_pointer, "VARCHAR"}, - {data_type_t::type_varchar, "VARCHAR"}, - {data_type_t::type_text, "TEXT"}, - {data_type_t::type_date, "DATE"}, - {data_type_t::type_time, "DATETIME"}, - {data_type_t::type_blob, "BLOB"}, - {data_type_t::type_null, "NULL"}, - {data_type_t::type_unknown, "UNKNOWN"} + {utils::basic_type::type_int8, "TINYINT"}, + {utils::basic_type::type_int16, "SMALLINT"}, + {utils::basic_type::type_int32, "INTEGER"}, + {utils::basic_type::type_int64, "BIGINT"}, + {utils::basic_type::type_uint8, "TINYINT"}, + {utils::basic_type::type_uint16, "INTEGER"}, + {utils::basic_type::type_uint32, "BIGINT"}, + {utils::basic_type::type_uint64, "BIGINT"}, + {utils::basic_type::type_float, "FLOAT"}, + {utils::basic_type::type_double, "DOUBLE"}, + {utils::basic_type::type_bool, "BOOLEAN"}, + {utils::basic_type::type_varchar, "VARCHAR"}, + {utils::basic_type::type_text, "TEXT"}, + {utils::basic_type::type_date, "DATE"}, + {utils::basic_type::type_time, "DATETIME"}, + {utils::basic_type::type_blob, "BLOB"}, + {utils::basic_type::type_null, "NULL"} }; sql_func_to_string_map sql_func_map_ { @@ -224,6 +202,10 @@ private: {sql_function_t::MIN, "MIN" }, {sql_function_t::MAX, "MAX" }, }; + + std::array bool_strings_ { + "0", "1" + }; }; } diff --git a/include/matador/sql/dialect_builder.hpp b/include/matador/sql/dialect_builder.hpp index a065df7..f2425d4 100644 --- a/include/matador/sql/dialect_builder.hpp +++ b/include/matador/sql/dialect_builder.hpp @@ -16,6 +16,7 @@ public: dialect_builder& with_data_type_replace_map(const dialect::data_type_to_string_map &data_type_replace_map); dialect_builder& with_placeholder_func(const dialect::next_placeholder_func &func); dialect_builder& with_default_schema_name(const std::string &schema_name); + dialect_builder& with_bool_strings(const std::string &true_string, const std::string &false_string); dialect build(); diff --git a/include/matador/sql/dialect_token.hpp b/include/matador/sql/dialect_token.hpp new file mode 100644 index 0000000..6c91697 --- /dev/null +++ b/include/matador/sql/dialect_token.hpp @@ -0,0 +1,54 @@ +#ifndef DIALECT_TOKEN_HPP +#define DIALECT_TOKEN_HPP + +#include + +namespace matador::sql { + +enum class dialect_token : uint8_t +{ + CREATE = 0, + DROP, + REMOVE, + INSERT, + UPDATE, + SELECT, + TABLE, + VALUES, + INSERT_VALUES, + COLUMNS, + COLUMN, + FROM, + JOIN, + ON, + INTO, + WHERE, + WHERE_CLAUSE, + AND, + OR, + LIKE, + ORDER_BY, + GROUP_BY, + ASC, + DESC, + LIMIT, + AS, + OFFSET, + DISTINCT, + SET, + UPDATE_VALUES, + NOT_NULL, + PRIMARY_KEY, + BEGIN, + COMMIT, + ROLLBACK, + START_QUOTE, + END_QUOTE, + STRING_QUOTE, + BEGIN_BINARY_DATA, + END_BINARY_DATA, + NONE +}; +} + +#endif //DIALECT_TOKEN_HPP diff --git a/include/matador/sql/entity.hpp b/include/matador/sql/entity.hpp deleted file mode 100644 index 1df60eb..0000000 --- a/include/matador/sql/entity.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef QUERY_ENTITY_HPP -#define QUERY_ENTITY_HPP - -#include - -namespace matador::sql { - -template < class Type > -class entity -{ -public: - using value_type = Type; - using pointer = value_type*; - using reference = value_type&; - - entity() = default; - explicit entity(Type *obj) - : obj_(obj) {} - entity(const entity&) = default; - entity& operator=(const entity&) = default; - entity(entity&&) noexcept = default; - entity& operator=(entity&&) noexcept = default; - ~entity() = default; - - void reset(Type *obj) { obj_.reset(obj); } - - pointer operator->() { return obj_.get(); } - const value_type* operator->() const { return obj_.get(); } - - reference operator*() { return *obj_; } - const value_type& operator*() const { return *obj_; } - - pointer get() { return obj_.get(); } - const value_type* get() const { return obj_.get(); } - - operator bool() const { return obj_.get() != nullptr; } // NOLINT(*-explicit-constructor) - -private: - std::shared_ptr obj_; -}; - -template -[[maybe_unused]] entity make_entity(Args&&... args) -{ - return entity(new Type(std::forward(args)...)); -} - -} -#endif //QUERY_ENTITY_HPP diff --git a/include/matador/sql/entity_query_builder.hpp b/include/matador/sql/entity_query_builder.hpp deleted file mode 100644 index 9c7c42c..0000000 --- a/include/matador/sql/entity_query_builder.hpp +++ /dev/null @@ -1,283 +0,0 @@ -#ifndef QUERY_ENTITY_QUERY_BUILDER_HPP -#define QUERY_ENTITY_QUERY_BUILDER_HPP - -#include "matador/sql/connection.hpp" -#include "matador/sql/condition.hpp" -#include "matador/sql/query_context.hpp" -#include "matador/sql/query.hpp" -#include "matador/sql/query_intermediates.hpp" -#include "matador/sql/value.hpp" - -#include "matador/utils/result.hpp" - -#include - -namespace matador::sql { - -struct join_columns -{ - std::string join_column; - std::string inverse_join_column; -}; - -class join_column_collector -{ -public: - template - join_columns collect() - { - join_columns_ = {}; - Type obj; - - matador::utils::access::process(*this, obj); - - return join_columns_; - } - template < class V > - void on_primary_key(const char * /*id*/, V &, typename std::enable_if::value && !std::is_same::value>::type* = 0) {} - void on_primary_key(const char * /*id*/, std::string &, size_t) {} - void on_revision(const char * /*id*/, unsigned long long &/*rev*/) {} - template - void on_attribute(const char * /*id*/, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} - template - void on_belongs_to(const char * /*id*/, Pointer &obj, const utils::foreign_attributes &attr) {} - template - void on_has_one(const char * /*id*/, Pointer &obj, const utils::foreign_attributes &attr) {} - template - void on_has_many(ContainerType &, const char *join_column, const utils::foreign_attributes &attr) {} - template - void on_has_many_to_many(const char * /*id*/, ContainerType &/*c*/, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &/*attr*/) - { - join_columns_.join_column = join_column; - join_columns_.inverse_join_column = inverse_join_column; - } - template - void on_has_many_to_many(const char * /*id*/, ContainerType &/*c*/, const utils::foreign_attributes &/*attr*/) {} - -private: - join_columns join_columns_; -}; - -struct entity_query_data { - std::string root_table_name; - std::string pk_column_; - std::vector columns; - std::vector joins; - std::unique_ptr where_clause; -}; - -enum class query_build_error : std::uint8_t { - Ok = 0, - UnknownType, - MissingPrimaryKey, - UnexpectedError -}; - -class query_builder_exception : public std::exception -{ -public: - explicit query_builder_exception(query_build_error error) : error_(error) {} - - [[nodiscard]] query_build_error error() const { return error_; } - -private: - const query_build_error error_; -}; - -class entity_query_builder -{ -public: - explicit entity_query_builder(const schema &scm) - : schema_(scm) {} - - template - utils::result build(const PrimaryKeyType &pk) { - const auto info = schema_.info(); - if (!info) { - return utils::error(query_build_error::UnknownType); - } - pk_ = pk; - table_info_stack_.push(info.value()); - entity_query_data_ = { info->name }; - EntityType obj; - try { - matador::utils::access::process(*this, obj); - - return {utils::ok(std::move(entity_query_data_))}; - } catch (const query_builder_exception &ex) { - return {utils::error(ex.error())}; - } catch (...) { - return {utils::error(query_build_error::UnexpectedError)}; - } - } - - template - utils::result build() { - const auto info = schema_.info(); - if (!info) { - return utils::error(query_build_error::UnknownType); - } - pk_ = nullptr; - table_info_stack_.push(info.value()); - entity_query_data_ = { info->name }; - EntityType obj; - try { - matador::utils::access::process(*this, obj); - - return {utils::ok(std::move(entity_query_data_))}; - } catch (const query_builder_exception &ex) { - return {utils::error(ex.error())}; - } catch (...) { - return {utils::error(query_build_error::UnexpectedError)}; - } - } - - template < class V > - void on_primary_key(const char *id, V &, typename std::enable_if::value && !std::is_same::value>::type* = 0) - { - push(id); - if (!is_root_entity()) { - return; - } - if (pk_.is_null()) { - entity_query_data_.pk_column_ = id; - } else if (pk_.is_integer()) { - entity_query_data_.where_clause = make_condition(column{table_info_stack_.top().name, id, ""} == *pk_.as()); - entity_query_data_.pk_column_ = id; - } - } - - void on_primary_key(const char *id, std::string &, size_t); - void on_revision(const char *id, unsigned long long &/*rev*/); - - template - void on_attribute(const char *id, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes) - { - push(id); - } - - template - void on_belongs_to(const char *id, Pointer &obj, const utils::foreign_attributes &attr) - { - on_foreign_object(id, obj, attr); - } - - template - void on_has_one(const char *id, Pointer &obj, const utils::foreign_attributes &attr) - { - on_foreign_object(id, obj, attr); - } - - template - void on_has_many(ContainerType &, const char *join_column, const utils::foreign_attributes &attr) - { - if (attr.fetch() == utils::fetch_type::EAGER) { - const auto info = schema_.info(); - if (!info) { - throw query_builder_exception{query_build_error::UnknownType}; - } - table_info_stack_.push(info.value()); - typename ContainerType::value_type::value_type obj; - matador::utils::access::process(*this , obj); - table_info_stack_.pop(); - - auto pk = info->prototype.primary_key(); - if (!pk) { - throw query_builder_exception{query_build_error::MissingPrimaryKey}; - } - - append_join({table_info_stack_.top().name, table_info_stack_.top().prototype.primary_key()->name()}, {info->name, join_column}); - } - } - - template - void on_has_many_to_many(const char *id, ContainerType &c, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &attr) - { - if (attr.fetch() != utils::fetch_type::EAGER) { - return; - } - const auto info = schema_.info(); - if (!info) { - throw query_builder_exception{query_build_error::UnknownType}; - } - table_info_stack_.push(info.value()); - typename ContainerType::value_type::value_type obj; - matador::utils::access::process(*this , obj); - table_info_stack_.pop(); - - auto pk = info->prototype.primary_key(); - if (!pk) { - throw query_builder_exception{query_build_error::MissingPrimaryKey}; - } - - append_join({table_info_stack_.top().name, table_info_stack_.top().prototype.primary_key()->name()}, {id, join_column}); - append_join({id, inverse_join_column}, {info->name, pk->name()}); - } - - template - void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &attr) - { - if (attr.fetch() != utils::fetch_type::EAGER) { - return; - } - const auto info = schema_.info(); - if (!info) { - throw query_builder_exception{query_build_error::UnknownType}; - } - table_info_stack_.push(info.value()); - typename ContainerType::value_type::value_type obj; - matador::utils::access::process(*this , obj); - table_info_stack_.pop(); - - auto pk = info->prototype.primary_key(); - if (!pk) { - throw query_builder_exception{query_build_error::MissingPrimaryKey}; - } - - const auto join_columns = join_column_collector_.collect(); - - append_join({table_info_stack_.top().name, table_info_stack_.top().prototype.primary_key()->name()}, {id, join_columns.inverse_join_column}); - append_join({id, join_columns.join_column}, {info->name, pk->name()}); - } - -private: - template - void on_foreign_object(const char *id, Pointer &, const utils::foreign_attributes &attr); - void push(const std::string &column_name); - [[nodiscard]] bool is_root_entity() const; - void append_join(const column &left, const column &right); - -private: - value pk_; - std::stack table_info_stack_; - const schema &schema_; - entity_query_data entity_query_data_; - int column_index{0}; - join_column_collector join_column_collector_; -}; - -template -void entity_query_builder::on_foreign_object(const char *id, Pointer &, const utils::foreign_attributes &attr) -{ - if (attr.fetch() == utils::fetch_type::EAGER) { - const auto info = schema_.info(); - if (!info) { - throw query_builder_exception{query_build_error::UnknownType}; - } - table_info_stack_.push(info.value()); - typename Pointer::value_type obj; - matador::utils::access::process(*this, obj); - table_info_stack_.pop(); - - auto pk = info->prototype.primary_key(); - if (!pk) { - throw query_builder_exception{query_build_error::MissingPrimaryKey}; - } - append_join({table_info_stack_.top().name, id}, {info->name, pk->name()}); - } else { - push(id); - } -} - -} -#endif //QUERY_ENTITY_QUERY_BUILDER_HPP diff --git a/include/matador/sql/error_code.hpp b/include/matador/sql/error_code.hpp new file mode 100644 index 0000000..cbab3c2 --- /dev/null +++ b/include/matador/sql/error_code.hpp @@ -0,0 +1,43 @@ +#ifndef SQL_ERROR_CODE_HPP +#define SQL_ERROR_CODE_HPP + +#include +#include + +namespace matador::sql { + +enum class error_code : uint8_t { + OK = 0, + INVALID_QUERY, + UNKNOWN_TABLE, + UNKNOWN_COLUMN, + BIND_FAILED, + EXECUTE_FAILED, + FETCH_FAILED, + PREPARE_FAILED, + DESCRIBE_FAILED, + TABLE_EXISTS_FAILED, + RETRIEVE_DATA_FAILED, + RESET_FAILED, + OPEN_ERROR, + CLOSE_ERROR, + FAILURE +}; + +class sql_category_impl final : public std::error_category +{ +public: + [[nodiscard]] const char* name() const noexcept override; + [[nodiscard]] std::string message(int ev) const override; +}; + +const std::error_category& sql_category(); +std::error_code make_error_code(error_code e); +std::error_condition make_error_condition(error_code e); + +} + +template <> +struct std::is_error_code_enum : true_type {}; + +#endif //SQL_ERROR_CODE_HPP diff --git a/include/matador/sql/executor.hpp b/include/matador/sql/executor.hpp new file mode 100644 index 0000000..e1bb750 --- /dev/null +++ b/include/matador/sql/executor.hpp @@ -0,0 +1,28 @@ +#ifndef EXECUTOR_HPP +#define EXECUTOR_HPP + +#include "matador/utils/error.hpp" +#include "matador/utils/result.hpp" + +#include + +namespace matador::sql { + +struct query_context; +class query_result_impl; +class statement; + +class executor { +public: + virtual ~executor(); + + [[nodiscard]] virtual const class dialect& dialect() const = 0; + [[nodiscard]] virtual utils::result execute(const query_context &ctx) const = 0; + [[nodiscard]] virtual utils::result, utils::error> fetch(const query_context &ctx) const = 0; + [[nodiscard]] virtual utils::result prepare(const query_context &ctx) const = 0; + [[nodiscard]] virtual std::string str(const query_context &ctx) const = 0; +}; + +} + +#endif //EXECUTOR_HPP diff --git a/include/matador/sql/field.hpp b/include/matador/sql/field.hpp index a749397..e95414b 100644 --- a/include/matador/sql/field.hpp +++ b/include/matador/sql/field.hpp @@ -1,23 +1,27 @@ #ifndef QUERY_FIELD_HPP #define QUERY_FIELD_HPP -#include "matador/sql/value.hpp" +#include "matador/utils/value.hpp" + +#include "matador/utils/basic_types.hpp" #include #include namespace matador::sql { -class field -{ +/** + * + */ +class field { public: explicit field(std::string name); template - field(std::string name, Type value, size_t size = 0, int index = -1) + field(std::string name, Type value, const size_t size = 0, const int index = -1) : name_(std::move(name)) , index_(index) , value_(value, size) {} - field(std::string name, data_type_t data_type, size_t size = 0, int index = -1); + field(std::string name, utils::basic_type dt, size_t size = 0, int index = -1); field(const field &x) = default; field& operator=(const field &x) = default; field(field &&x) noexcept; @@ -35,8 +39,7 @@ public: [[nodiscard]] int index() const; template - std::optional as() const - { + std::optional as() const { return value_.as(); } @@ -49,14 +52,12 @@ public: [[nodiscard]] bool is_varchar() const; [[nodiscard]] bool is_blob() const; [[nodiscard]] bool is_null() const; - [[nodiscard]] bool is_unknown() const; friend std::ostream& operator<<(std::ostream &out, const field &col); private: template - void process(Operator &op) - { + void process(Operator &op) { op.on_attribute(name_.c_str(), value_, value_.size()); } @@ -66,7 +67,7 @@ private: std::string name_; int index_{-1}; - value value_; + utils::value value_; }; } diff --git a/include/matador/sql/has_many_to_many_relation.hpp b/include/matador/sql/has_many_to_many_relation.hpp deleted file mode 100644 index 7597192..0000000 --- a/include/matador/sql/has_many_to_many_relation.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef QUERY_HAS_MANY_TO_MANY_RELATION_HPP -#define QUERY_HAS_MANY_TO_MANY_RELATION_HPP - -#include "matador/sql/entity.hpp" - -#include "matador/utils/access.hpp" -#include "matador/utils/foreign_attributes.hpp" - -namespace matador::sql { - -template < class LocalType, class ForeignType > -class has_many_to_many_relation -{ -public: - has_many_to_many_relation() = default; - has_many_to_many_relation(std::string local_name, std::string remote_name) - : local_name_(std::move(local_name)) - , remote_name_(std::move(remote_name)) {} - - template - void process(Operator &op) { - namespace field = matador::utils::access; - field::belongs_to(op, local_name_.c_str(), local_, utils::default_foreign_attributes); - field::belongs_to(op, remote_name_.c_str(), remote_, utils::default_foreign_attributes); - } - - entity local() const { return local_; } - entity remote() const { return remote_; } - -private: - std::string local_name_; - std::string remote_name_; - sql::entity local_; - sql::entity remote_; -}; - -} -#endif //QUERY_HAS_MANY_TO_MANY_RELATION_HPP diff --git a/include/matador/sql/interface/connection_impl.hpp b/include/matador/sql/interface/connection_impl.hpp new file mode 100644 index 0000000..0b147d3 --- /dev/null +++ b/include/matador/sql/interface/connection_impl.hpp @@ -0,0 +1,57 @@ +#ifndef QUERY_CONNECTION_IMPL_HPP +#define QUERY_CONNECTION_IMPL_HPP + +#include + +#include "matador/sql/column_definition.hpp" +#include "matador/sql/connection_info.hpp" +#include "matador/sql/query_context.hpp" +#include "matador/utils/error.hpp" +#include "matador/utils/result.hpp" +#include "matador/utils/version.hpp" + +namespace matador::utils { +using blob = std::vector; +} + +namespace matador::sql { + +class query_result_impl; +class statement_impl; + +class connection_impl +{ +public: + virtual ~connection_impl() = default; + + virtual utils::result open() = 0; + virtual utils::result close() = 0; + [[nodiscard]] virtual utils::result is_open() const = 0; + [[nodiscard]] virtual utils::result is_valid() const = 0; + + [[nodiscard]] virtual utils::result client_version() const = 0; + [[nodiscard]] virtual utils::result server_version() const = 0; + + virtual utils::result execute(const std::string &stmt) = 0; + virtual utils::result, utils::error> fetch(const query_context &context) = 0; + virtual utils::result, utils::error> prepare(const query_context &context) = 0; + + virtual utils::result, utils::error> describe(const std::string &table) = 0; + virtual utils::result exists(const std::string &schema_name, const std::string &table_name) = 0; + + [[nodiscard]] const class dialect &dialect() const; + + [[nodiscard]] virtual std::string to_escaped_string(const utils::blob &value) const = 0; + +protected: + explicit connection_impl(const connection_info &info); + + [[nodiscard]] const connection_info &info() const; + +private: + std::reference_wrapper info_; + std::reference_wrapper dialect_; +}; + +} +#endif //QUERY_CONNECTION_IMPL_HPP diff --git a/include/matador/sql/parameter_binder.hpp b/include/matador/sql/interface/parameter_binder.hpp similarity index 61% rename from include/matador/sql/parameter_binder.hpp rename to include/matador/sql/interface/parameter_binder.hpp index 221d264..a23f324 100644 --- a/include/matador/sql/parameter_binder.hpp +++ b/include/matador/sql/interface/parameter_binder.hpp @@ -6,23 +6,21 @@ #include #include -namespace matador::sql { +namespace matador::sql::interface { class parameter_binder { public: virtual ~parameter_binder() = default; - virtual void bind(size_t pos, char) = 0; - virtual void bind(size_t pos, short) = 0; - virtual void bind(size_t pos, int) = 0; - virtual void bind(size_t pos, long) = 0; - virtual void bind(size_t pos, long long) = 0; - virtual void bind(size_t pos, unsigned char) = 0; - virtual void bind(size_t pos, unsigned short) = 0; - virtual void bind(size_t pos, unsigned int) = 0; - virtual void bind(size_t pos, unsigned long) = 0; - virtual void bind(size_t pos, unsigned long long) = 0; + virtual void bind(size_t pos, int8_t) = 0; + virtual void bind(size_t pos, int16_t) = 0; + virtual void bind(size_t pos, int32_t) = 0; + virtual void bind(size_t pos, int64_t) = 0; + virtual void bind(size_t pos, uint8_t) = 0; + virtual void bind(size_t pos, uint16_t) = 0; + virtual void bind(size_t pos, uint32_t) = 0; + virtual void bind(size_t pos, uint64_t) = 0; virtual void bind(size_t pos, bool) = 0; virtual void bind(size_t pos, float) = 0; virtual void bind(size_t pos, double) = 0; diff --git a/include/matador/sql/interface/query_result_reader.hpp b/include/matador/sql/interface/query_result_reader.hpp new file mode 100644 index 0000000..dceeafe --- /dev/null +++ b/include/matador/sql/interface/query_result_reader.hpp @@ -0,0 +1,35 @@ +#ifndef QUERY_QUERY_RESULT_READER_HPP +#define QUERY_QUERY_RESULT_READER_HPP + +#include "matador/sql/internal/object_result_binder.hpp" + +#include "matador/utils/attribute_reader.hpp" +#include "matador/utils/error.hpp" +#include "matador/utils/result.hpp" + +namespace matador::sql { + +class query_result_reader : public utils::attribute_reader +{ +public: + [[nodiscard]] virtual size_t column_count() const = 0; + [[nodiscard]] virtual const char* column(size_t index) const = 0; + [[nodiscard]] virtual utils::result fetch() = 0; + [[nodiscard]] virtual size_t start_column_index() const = 0; + + template + void bind(Type &obj) { + object_binder_.reset(); + object_binder_.bind(obj, result_binder()); + } + +protected: + virtual attribute_reader& result_binder() = 0; + +private: + // detail::empty_binder empty_result_binder_; + object_result_binder object_binder_; +}; + +} +#endif //QUERY_QUERY_RESULT_READER_HPP diff --git a/include/matador/sql/interface/statement_impl.hpp b/include/matador/sql/interface/statement_impl.hpp new file mode 100644 index 0000000..a7e5e82 --- /dev/null +++ b/include/matador/sql/interface/statement_impl.hpp @@ -0,0 +1,62 @@ +#ifndef QUERY_STATEMENT_IMPL_HPP +#define QUERY_STATEMENT_IMPL_HPP + +#include "matador/sql/query_context.hpp" +#include "matador/sql/internal/query_result_impl.hpp" +#include "matador/sql/object_parameter_binder.hpp" + +#include "matador/utils/data_type_traits.hpp" + +#include + +namespace matador::sql { + +class sql_error; + +class statement_impl +{ +protected: + explicit statement_impl(query_context query); + +public: + virtual ~statement_impl() = default; + + virtual utils::result execute() = 0; + virtual utils::result, utils::error> fetch() = 0; + + template < class Type > + void bind_object(Type &obj) + { + object_parameter_binder object_binder_; + object_binder_.reset(start_index()); + object_binder_.bind(obj, binder()); + } + + template < class Type > + void bind(const size_t pos, Type &val) + { + utils::data_type_traits::bind_value(binder(), adjust_index(pos), val); + } + + void bind(size_t pos, const char *value, size_t size); + void bind(size_t pos, std::string &val, size_t size); + + virtual void reset() = 0; + + [[nodiscard]] const std::vector& bind_vars() const; + [[nodiscard]] bool is_valid_host_var(const std::string &host_var, size_t pos) const; + +protected: + virtual utils::attribute_writer& binder() = 0; + [[nodiscard]] virtual size_t start_index() const; + [[nodiscard]] virtual size_t adjust_index(size_t index) const; + +protected: + friend class statement; + + query_context query_; +}; + +} + +#endif //QUERY_STATEMENT_IMPL_HPP diff --git a/include/matador/sql/internal/object_result_binder.hpp b/include/matador/sql/internal/object_result_binder.hpp new file mode 100644 index 0000000..be2fed0 --- /dev/null +++ b/include/matador/sql/internal/object_result_binder.hpp @@ -0,0 +1,137 @@ +#ifndef MATADOR_OBJECT_RESULT_BINDER_HPP +#define MATADOR_OBJECT_RESULT_BINDER_HPP + +#include "matador/utils/attribute_reader.hpp" +#include "matador/utils/default_type_traits.hpp" + +#include "matador/utils/access.hpp" +#include "matador/utils/field_attributes.hpp" +#include "matador/utils/foreign_attributes.hpp" + +namespace matador::sql { + +namespace detail { + +class fk_result_binder +{ +public: + template + void bind(Type &obj, const char *id, size_t column_index, utils::attribute_reader &binder) + { + binder_ = &binder; + index_ = column_index; + id_ = id; + access::process(*this, obj); + id_ = nullptr; + binder_ = nullptr; + } + + template + void on_primary_key(const char *id, ValueType &value, std::enable_if_t && !std::is_same_v>* = nullptr); + void on_primary_key(const char *id, std::string &value, size_t size); + void on_revision(const char * /*id*/, unsigned long long &/*rev*/) {} + + template < class Type > + void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} + template < class Pointer > + void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) {} + template < class Pointer > + void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) {} + + template + void on_has_many(const char * /*id*/, + ContainerType &/*c*/, + const char * /*join_column*/, + const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) {} + template + void on_has_many_to_many(const char * /*id*/, + ContainerType &/*c*/, + const char * /*join_column*/, + const char * /*inverse_join_column*/, + const utils::foreign_attributes &/*attr*/) {} + template + void on_has_many_to_many(const char * /*id*/, + ContainerType &/*c*/, + const utils::foreign_attributes &/*attr*/) {} + +private: + utils::attribute_reader *binder_{}; + size_t index_{0}; + const char *id_{}; +}; + +} + +class object_result_binder { +public: + template + void bind(Type &obj, utils::attribute_reader &binder) { + binder_ = &binder; + access::process(*this, obj); + binder_ = nullptr; + } + + void reset(); + + template < class Type > + void on_primary_key(const char *id, Type &val, std::enable_if_t && !std::is_same_v>* = nullptr) + { + utils::data_type_traits::read_value(*binder_, id, index_++, val); + } + void on_primary_key(const char *id, std::string &, size_t size); + void on_revision(const char *id, uint64_t &/*rev*/); + + template + void on_attribute(const char *id, Type &val, const utils::field_attributes &/*attr*/ = utils::null_attributes) + { + utils::data_type_traits::read_value(*binder_, id, index_++, val); + } + void on_attribute(const char *id, char *value, const utils::field_attributes &attr = utils::null_attributes); + void on_attribute(const char *id, std::string &value, const utils::field_attributes &attr = utils::null_attributes); + void on_attribute(const char *id, utils::value &val, const utils::field_attributes &attr = utils::null_attributes); + + template class Pointer> + void on_belongs_to(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) + { + fk_result_binder_.bind(*x, id, index_++, *binder_); + } + template class Pointer> + void on_has_one(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) + { + fk_result_binder_.bind(*x, id, index_++, *binder_); + } + template + void on_has_many(const char * /*id*/, + ContainerType &/*c*/, + const char * /*join_column*/, + const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) {} + template + void on_has_many_to_many(const char * /*id*/, + ContainerType &/*c*/, + const char * /*join_column*/, + const char * /*inverse_join_column*/, + const utils::foreign_attributes &/*attr*/) {} + template + void on_has_many_to_many(const char * /*id*/, + ContainerType &/*c*/, + const utils::foreign_attributes &/*attr*/) {} + +private: + utils::attribute_reader *binder_{}; + size_t index_{0}; + detail::fk_result_binder fk_result_binder_; +}; + +namespace detail { + +template +void fk_result_binder::on_primary_key(const char * /*id*/, ValueType &value, std::enable_if_t && !std::is_same_v> *) +{ + utils::data_type_traits::read_value(*binder_, id_, index_++, value); +} + +} + +} + +#endif //MATADOR_OBJECT_RESULT_BINDER_HPP diff --git a/include/matador/sql/query_result_impl.hpp b/include/matador/sql/internal/query_result_impl.hpp similarity index 53% rename from include/matador/sql/query_result_impl.hpp rename to include/matador/sql/internal/query_result_impl.hpp index 581bdff..0aaab00 100644 --- a/include/matador/sql/query_result_impl.hpp +++ b/include/matador/sql/internal/query_result_impl.hpp @@ -4,18 +4,20 @@ #include "matador/utils/access.hpp" #include "matador/utils/field_attributes.hpp" #include "matador/utils/foreign_attributes.hpp" +#include "matador/utils/default_type_traits.hpp" + +#include "matador/sql/interface/query_result_reader.hpp" -#include "matador/sql/any_type.hpp" -#include "matador/sql/query_result_reader.hpp" #include "matador/sql/column_definition.hpp" -#include "matador/sql/data_type_traits.hpp" #include #include -namespace matador::sql { - +namespace matador::utils { class value; +} + +namespace matador::sql { namespace detail { class pk_reader @@ -24,14 +26,14 @@ public: explicit pk_reader(query_result_reader &reader); template - void read(Type &obj, size_t column_index) + void read(Type &obj, const size_t column_index) { column_index_ = column_index; - utils::access::process(*this, obj); + access::process(*this, obj); } template - void on_primary_key(const char *id, ValueType &value, typename std::enable_if::value && !std::is_same::value>::type* = 0); + void on_primary_key(const char *id, ValueType &value, std::enable_if_t && !std::is_same_v>* = nullptr); void on_primary_key(const char *id, std::string &value, size_t size); void on_revision(const char * /*id*/, unsigned long long &/*rev*/) {} @@ -43,7 +45,7 @@ public: void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {} template - void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {} + void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {} template void on_has_many_to_many(const char *id, ContainerType &c, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &/*attr*/) {} template @@ -59,66 +61,91 @@ private: class query_result_impl { public: - query_result_impl(std::unique_ptr &&reader, std::vector prototype); + query_result_impl(std::unique_ptr &&reader, std::vector &&prototype, size_t column_index = 0); + query_result_impl(std::unique_ptr &&reader, const std::vector &prototype, size_t column_index = 0); template - void on_primary_key(const char *id, ValueType &value, typename std::enable_if::value && !std::is_same::value>::type* = 0) + void on_primary_key(const char *id, ValueType &value, std::enable_if_t && !std::is_same_v>* = nullptr) { - data_type_traits::read_value(*reader_, id, column_index_++, value); + utils::data_type_traits::read_value(*reader_, id, column_index_++, value); } void on_primary_key(const char *id, std::string &value, size_t size); - void on_revision(const char *id, unsigned long long &rev); + void on_revision(const char *id, uint64_t &rev); template < class Type > void on_attribute(const char *id, Type &x, const utils::field_attributes &/*attr*/ = utils::null_attributes) { - data_type_traits::read_value(*reader_, id, column_index_++, x); + utils::data_type_traits::read_value(*reader_, id, column_index_++, x); } void on_attribute(const char *id, char *value, const utils::field_attributes &attr = utils::null_attributes); void on_attribute(const char *id, std::string &value, const utils::field_attributes &attr = utils::null_attributes); - void on_attribute(const char *id, value &val, const utils::field_attributes &attr = utils::null_attributes); + void on_attribute(const char *id, utils::value &val, const utils::field_attributes &attr = utils::null_attributes); template < class Pointer > void on_belongs_to(const char * /*id*/, Pointer &x, const utils::foreign_attributes &attr) { - if (!x.get()) { - x.reset(new typename Pointer::value_type); + if (x.empty()) { + x = new typename Pointer::value_type; } if (attr.fetch() == utils::fetch_type::LAZY) { pk_reader_.read(*x, column_index_++); } else { - utils::access::process(*this, *x); + access::process(*this, *x); } } template < class Pointer > void on_has_one(const char * /*id*/, Pointer &x, const utils::foreign_attributes &attr) { - if (!x.get()) { - x.reset(new typename Pointer::value_type); + if (x.empty()) { + x = new typename Pointer::value_type; } if (attr.fetch() == utils::fetch_type::LAZY) { pk_reader_.read(*x, column_index_++); } else { - utils::access::process(*this, *x); + access::process(*this, *x); } } template - void on_has_many(const char *, ContainerType &, const char *, const char *, const utils::foreign_attributes &/*attr*/) {} + void on_has_many_to_many(const char *, ContainerType &, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {} + template + void on_has_many_to_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {} + template + void on_has_many(const char * /*id*/, ContainerType &cont, const char * /*join_column*/, const utils::foreign_attributes &attr) { + if ( attr.fetch() == utils::fetch_type::LAZY ) { + // pk_reader_.read(*id, column_index_++); + } else { + auto obj = std::make_unique(); + // typename ContainerType::value_type x(new typename ContainerType::value_type::value_type); + access::process(*this, *obj); + auto ptr = typename ContainerType::value_type(obj.release()); + const auto pk = ptr.primary_key(); + if (ptr.primary_key().is_valid()) { + cont.push_back(ptr); + } + } + } template void on_has_many(const char *, ContainerType &, const utils::foreign_attributes &/*attr*/) {} template - void bind(const Type &) {} + void bind(const Type &obj) + { + reader_->bind(obj); + } template bool fetch(Type &obj) { - column_index_ = 0; - if (!reader_->fetch()) { + column_index_ = reader_->start_column_index(); + auto fetched = reader_->fetch(); + if (!fetched.is_ok()) { return false; } - matador::utils::access::process(*this, obj); + if (!*fetched) { + return false; + } + access::process(*this, obj); return true; } @@ -134,9 +161,9 @@ protected: namespace detail { template -void detail::pk_reader::on_primary_key(const char *id, ValueType &value, typename std::enable_if::value && !std::is_same::value>::type *) +void detail::pk_reader::on_primary_key(const char *id, ValueType &value, std::enable_if_t && !std::is_same_v> *) { - data_type_traits::read_value(reader_, id, column_index_++, value); + utils::data_type_traits::read_value(reader_, id, column_index_++, value); } } diff --git a/include/matador/sql/key_value_pair.hpp b/include/matador/sql/key_value_pair.hpp deleted file mode 100644 index ca78fa1..0000000 --- a/include/matador/sql/key_value_pair.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef QUERY_KEY_VALUE_PAIR_HPP -#define QUERY_KEY_VALUE_PAIR_HPP - -#include "matador/sql/any_type.hpp" -#include "matador/sql/column.hpp" - -namespace matador::sql { - -class key_value_pair -{ -public: - key_value_pair(const sql::column &col, any_type value); - key_value_pair(std::string name, any_type value); - key_value_pair(const char *name, any_type value); - - [[nodiscard]] const std::string& name() const; - [[nodiscard]] const any_type& value() const; - -private: - std::string name_; - any_type value_; -}; - -} -#endif //QUERY_KEY_VALUE_PAIR_HPP diff --git a/include/matador/sql/noop_connection.hpp b/include/matador/sql/noop_connection.hpp deleted file mode 100644 index 182166a..0000000 --- a/include/matador/sql/noop_connection.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef QUERY_NOOP_CONNECTION_HPP -#define QUERY_NOOP_CONNECTION_HPP - -#include "matador/sql/connection_impl.hpp" - -namespace matador::sql { - -class noop_connection final : public connection_impl -{ -public: - explicit noop_connection(const connection_info &info); - - void open() override; - void close() override; - bool is_open() override; - size_t execute(const std::string &stmt) override; - std::unique_ptr fetch(const std::string &stmt) override; - std::unique_ptr prepare(query_context context) override; - std::vector describe(const std::string &table) override; - bool exists(const std::string &schema_name, const std::string &table_name) override; - -private: - bool is_open_{false}; -}; -} -#endif //QUERY_NOOP_CONNECTION_HPP diff --git a/include/matador/sql/object_parameter_binder.hpp b/include/matador/sql/object_parameter_binder.hpp index 8b704f3..b697452 100644 --- a/include/matador/sql/object_parameter_binder.hpp +++ b/include/matador/sql/object_parameter_binder.hpp @@ -1,49 +1,61 @@ #ifndef QUERY_OBJECT_PARAMETER_BINDER_HPP #define QUERY_OBJECT_PARAMETER_BINDER_HPP -#include "matador/sql/parameter_binder.hpp" -#include "matador/sql/data_type_traits.hpp" +#include "matador/utils/attribute_writer.hpp" +#include "matador/utils/default_type_traits.hpp" #include "matador/utils/access.hpp" -#include "matador/utils/cascade_type.hpp" #include "matador/utils/field_attributes.hpp" +#include "matador/utils/foreign_attributes.hpp" #include namespace matador::sql { namespace detail { + class fk_binder { public: - explicit fk_binder(parameter_binder &binder); - template - void bind(Type &obj, size_t column_index) + void bind(Type &obj, const size_t column_index, utils::attribute_writer &binder) { + binder_ = &binder; index_ = column_index; - utils::access::process(*this, obj); + access::process(*this, obj); + binder_ = nullptr; } template - void on_primary_key(const char *id, ValueType &value, typename std::enable_if::value && !std::is_same::value>::type* = 0); + void on_primary_key(const char *id, ValueType &value, std::enable_if_t && !std::is_same_v>* = nullptr); void on_primary_key(const char *id, std::string &value, size_t size); void on_revision(const char * /*id*/, unsigned long long &/*rev*/) {} template < class Type > void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} template < class Pointer > - void on_belongs_to(const char * /*id*/, Pointer &/*x*/, utils::cascade_type) {} + void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) {} template < class Pointer > - void on_has_one(const char * /*id*/, Pointer &/*x*/, utils::cascade_type) {} + void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) {} template - void on_has_many(const char *, ContainerType &, const char *, const char *, utils::cascade_type) {} + void on_has_many(const char * /*id*/, + ContainerType &/*c*/, + const char * /*join_column*/, + const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) {} template - void on_has_many(const char *, ContainerType &, utils::cascade_type) {} + void on_has_many_to_many(const char * /*id*/, + ContainerType &/*c*/, + const char * /*join_column*/, + const char * /*inverse_join_column*/, + const utils::foreign_attributes &/*attr*/) {} + template + void on_has_many_to_many(const char * /*id*/, + ContainerType &/*c*/, + const utils::foreign_attributes &/*attr*/) {} private: - parameter_binder &binder_; + utils::attribute_writer *binder_{}; size_t index_{0}; }; @@ -52,41 +64,57 @@ private: class object_parameter_binder { public: - explicit object_parameter_binder(parameter_binder &binder); + template + void bind(Type &obj, utils::attribute_writer &binder) { + binder_ = &binder; + access::process(*this, obj); + binder_ = nullptr; + } - void reset(); + void reset(size_t start_index); template < class Type > - void on_primary_key(const char *id, Type &val, typename std::enable_if::value && !std::is_same::value>::type* = 0) + void on_primary_key(const char * /*id*/, Type &val, std::enable_if_t && !std::is_same_v>* = nullptr) { - data_type_traits::bind_value(binder_, index_++, val); + utils::data_type_traits::bind_value(*binder_, index_++, val); } void on_primary_key(const char *id, std::string &, size_t size); - void on_revision(const char *id, unsigned long long &/*rev*/); + void on_revision(const char *id, uint64_t &/*rev*/); template - void on_attribute(const char *id, Type &val, const utils::field_attributes &/*attr*/ = utils::null_attributes) + void on_attribute(const char * /*id*/, Type &val, const utils::field_attributes &/*attr*/ = utils::null_attributes) { - data_type_traits::bind_value(binder_, index_++, val); + utils::data_type_traits::bind_value(*binder_, index_++, val); } template class Pointer> - void on_belongs_to(const char *id, Pointer &x, utils::cascade_type) + void on_belongs_to(const char * /*id*/, Pointer &x, const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) { - fk_binder_.bind(index_++, x); + fk_binder_.bind(*x, index_++, *binder_); } template class Pointer> - void on_has_one(const char *id, Pointer &x, utils::cascade_type) + void on_has_one(const char * /*id*/, Pointer &x, const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) { - fk_binder_.bind(index_++, x); + fk_binder_.bind(*x, index_++, *binder_); } template - void on_has_many(const char *, ContainerType &, const char *, const char *, utils::cascade_type) {} + void on_has_many(const char * /*id*/, + ContainerType &/*c*/, + const char * /*join_column*/, + const utils::foreign_attributes &/*attr*/ = utils::default_foreign_attributes) {} template - void on_has_many(const char *, ContainerType &, utils::cascade_type) {} + void on_has_many_to_many(const char * /*id*/, + ContainerType &/*c*/, + const char * /*join_column*/, + const char * /*inverse_join_column*/, + const utils::foreign_attributes &/*attr*/) {} + template + void on_has_many_to_many(const char * /*id*/, + ContainerType &/*c*/, + const utils::foreign_attributes &/*attr*/) {} private: - parameter_binder &binder_; + utils::attribute_writer *binder_{}; size_t index_{0}; detail::fk_binder fk_binder_; }; @@ -94,9 +122,9 @@ private: namespace detail { template -void fk_binder::on_primary_key(const char *id, ValueType &value, typename std::enable_if::value && !std::is_same::value>::type *) +void fk_binder::on_primary_key(const char * /*id*/, ValueType &value, std::enable_if_t && !std::is_same_v> *) { - data_type_traits::bind_value(binder_, index_++, value); + utils::data_type_traits::bind_value(*binder_, index_++, value); } } diff --git a/include/matador/sql/placeholder_generator.hpp b/include/matador/sql/placeholder_generator.hpp deleted file mode 100644 index e658520..0000000 --- a/include/matador/sql/placeholder_generator.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef QUERY_PLACEHOLDER_GENERATOR_HPP -#define QUERY_PLACEHOLDER_GENERATOR_HPP - -#include "matador/sql/any_type.hpp" - -#include "matador/utils/cascade_type.hpp" -#include "matador/utils/field_attributes.hpp" - -#include - -namespace matador::sql { - -class placeholder_generator -{ -public: - template < class V > - void on_primary_key(const char *id, V &val, typename std::enable_if::value && !std::is_same::value>::type* = 0) - { - placeholder_values.emplace_back(_); - } - void on_primary_key(const char *id, std::string &, size_t); - void on_revision(const char *id, unsigned long long &/*rev*/); - - template - void on_attribute(const char *id, Type &val, const utils::field_attributes &/*attr*/ = utils::null_attributes) - { - placeholder_values.emplace_back(_); - } - - template class Pointer> - void on_belongs_to(const char *id, Pointer &x, utils::cascade_type) - { - placeholder_values.emplace_back(_); - } - template class Pointer> - void on_has_one(const char *id, Pointer &x, utils::cascade_type) - { - placeholder_values.emplace_back(_); - } - template - void on_has_many(const char *, ContainerType &, const char *, const char *, utils::cascade_type) {} - template - void on_has_many(const char *, ContainerType &, utils::cascade_type) {} - - std::vector placeholder_values; -}; - -} -#endif //QUERY_PLACEHOLDER_GENERATOR_HPP diff --git a/include/matador/sql/query.hpp b/include/matador/sql/query.hpp deleted file mode 100644 index 9c7058b..0000000 --- a/include/matador/sql/query.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef QUERY_QUERY_HPP -#define QUERY_QUERY_HPP - -#include "matador/sql/query_intermediates.hpp" - -namespace matador::sql { - -class connection; - -class query -{ -public: - explicit query(connection &db, const sql::schema &schema); - query(const query &) = delete; - query& operator=(const query &) = delete; - - query_create_intermediate create(); - query_drop_intermediate drop(); - query_select_intermediate select(std::initializer_list columns); - query_select_intermediate select(const std::vector& columns); - query_select_intermediate select(std::vector columns, std::initializer_list additional_columns); - query_insert_intermediate insert(); - query_update_intermediate update(const sql::table &table); - query_delete_intermediate remove(); - -private: - connection &connection_; - const sql::schema &schema_; -}; - -} -#endif //QUERY_QUERY_HPP diff --git a/include/matador/sql/query.puml b/include/matador/sql/query.puml deleted file mode 100644 index b95ce47..0000000 --- a/include/matador/sql/query.puml +++ /dev/null @@ -1,89 +0,0 @@ -@startuml - -scale 800 width -state Query - -state Create -Create : Create database table -state Drop -Drop : Drop database table -state Select -Select: Query rows -Select: Add columns -state Insert -Insert: Insert rows -state Update -Update: Update rows -Update: Set table name -state Delete -Delete: Delete items from table - -state Table -Table: Set table name -state Into -Into: Set table name -Into: Add columns -state From -From: Set table name - -state Join -Join: Set table -Join: Set join type (inner left, right, outer) -state Where -Where: Add where clause -state Set -Set: Set column value pairs -state Values -Values: Set values - -state On: -On: Set Expression - -state GroupBy -GroupBy: Add column names -state OrderBy -OrderBy: Add expression -state Limit -Limit: Add number of max result elements - -[*] --> Query -Query --> Create -Query --> Drop -Query --> Select -Query --> Insert -Query --> Update -Query --> Delete - -Create --> Table -Drop --> Table -Select --> From -Insert --> Into -Delete --> From - -Into --> Values -Update --> Set -Set -> Where -From --> Where -From --> OrderBy -From --> GroupBy -From --> Join -Join --> On -On --> Where - -Where --> GroupBy -Where --> OrderBy -Where --> Limit - -GroupBy --> OrderBy -OrderBy --> Limit - -Table --> [*] -Values ---> [*] -Where --> [*] -Set --> [*] -From --> [*] -Limit --> [*] -OrderBy --> [*] -GroupBy --> [*] - -@enduml diff --git a/include/matador/sql/query_builder.hpp b/include/matador/sql/query_builder.hpp deleted file mode 100644 index bf392aa..0000000 --- a/include/matador/sql/query_builder.hpp +++ /dev/null @@ -1,176 +0,0 @@ -#ifndef QUERY_QUERY_BUILDER_HPP -#define QUERY_QUERY_BUILDER_HPP - -#include "matador/sql/basic_condition.hpp" -#include "matador/sql/column_definition.hpp" -#include "matador/sql/column.hpp" -#include "matador/sql/dialect.hpp" -#include "matador/sql/key_value_pair.hpp" -#include "matador/sql/query_context.hpp" -#include "matador/sql/record.hpp" - -#include -#include -#include -#include -#include - -namespace matador::sql { - -namespace detail { -struct any_type_to_string_visitor -{ - explicit any_type_to_string_visitor(const dialect &d, query_context &query); - - void operator()(char &x) { to_string(x); } - void operator()(short &x) { to_string(x); } - void operator()(int &x) { to_string(x); } - void operator()(long &x) { to_string(x); } - void operator()(long long &x) { to_string(x); } - void operator()(unsigned char &x) { to_string(x); } - void operator()(unsigned short &x) { to_string(x); } - void operator()(unsigned int &x) { to_string(x); } - void operator()(unsigned long &x) { to_string(x); } - void operator()(unsigned long long &x) { to_string(x); } - void operator()(bool &x) { to_string(x); } - void operator()(float &x) { to_string(x); } - void operator()(double &x) { to_string(x); } - void operator()(const char *x) { to_string(x); } - void operator()(std::string &x) { to_string(x); } - void operator()(utils::blob &x) { to_string(x); } - void operator()(placeholder &x) { to_string(x); } - - template - void to_string(Type &val) - { - result = std::to_string(val); - } - void to_string(const char *val); - void to_string(std::string &val); - void to_string(utils::blob &val); - void to_string(placeholder &val); - - const dialect &d; - query_context &query; - std::string result; -}; - -} - -column alias(const std::string &column, const std::string &as); -column alias(column &&col, const std::string &as); -column count(const std::string &column); -column count_all(); - -enum class join_type_t { - INNER, OUTER, LEFT, RIGHT -}; - -class query_builder -{ -private: - enum class state_t { - QUERY_INIT, - QUERY_CREATE, - QUERY_TABLE_CREATE, - QUERY_TABLE_DROP, - QUERY_DROP, - QUERY_SELECT, - QUERY_INSERT, - QUERY_UPDATE, - QUERY_DELETE, - QUERY_SET, - QUERY_FROM, - QUERY_JOIN, - QUERY_ON, - QUERY_INTO, - QUERY_WHERE, - QUERY_VALUES, - QUERY_ORDER_BY, - QUERY_ORDER_DIRECTION, - QUERY_GROUP_BY, - QUERY_OFFSET, - QUERY_LIMIT, - QUERY_FINISH - }; - - enum class command_t { - UNKNOWN, /**< Unknown query command */ - CREATE, /**< Create query command */ - DROP, /**< Drop query command */ - SELECT, /**< Select query command */ - INSERT, /**< Insert query command */ - UPDATE, /**< Update query command */ - REMOVE /**< Remove query command */ - }; - - struct query_part - { - query_part(dialect::token_t t, std::string p) - : token(t), part(std::move(p)) {} - - dialect::token_t token; - std::string part; - }; - -public: - explicit query_builder(const dialect &d); - - query_builder& create(); - query_builder& drop(); - query_builder& select(std::initializer_list columns); - query_builder& select(const std::vector &columns); - query_builder& insert(); - query_builder& update(const std::string &table); - query_builder& remove(); - - query_builder& table(const std::string &table, std::initializer_list columns); - query_builder& table(const std::string &table, const std::vector &columns); - query_builder& table(const std::string &table); - query_builder& into(const std::string &table, std::initializer_list column_names); - query_builder& into(const std::string &table, const std::vector &column_names); - query_builder& values(std::initializer_list values); - query_builder& values(const std::vector &values); - query_builder& from(const std::string &table, const std::string &as = ""); - query_builder& join(const std::string &table, join_type_t, const std::string &as = ""); - query_builder& on(const std::string &column, const std::string &join_column); - query_builder& set(std::initializer_list key_values); - query_builder& set(const std::vector &key_values); - query_builder& where(const basic_condition &cond); - query_builder& order_by(const std::string &column); - query_builder& group_by(const std::string &column); - query_builder& asc(); - query_builder& desc(); - query_builder& offset(size_t count); - query_builder& limit(size_t count); - - query_context compile(); - -private: - void transition_to(state_t next); - void initialize(command_t cmd, state_t state); - -private: - const dialect &dialect_; - - command_t command_{command_t::UNKNOWN}; - state_t state_{state_t::QUERY_INIT}; - - std::vector query_parts_; - - detail::any_type_to_string_visitor value_to_string_; - - query_context query_; - - using query_state_set = std::unordered_set; - using query_state_transition_map = std::unordered_map; - using query_state_to_string_map = std::unordered_map; - using query_command_to_string_map = std::unordered_map; - - static query_state_transition_map transitions_; - static query_state_to_string_map state_strings_; - static query_command_to_string_map command_strings_; -}; - -} -#endif //QUERY_QUERY_BUILDER_HPP diff --git a/include/matador/sql/query_compiler.hpp b/include/matador/sql/query_compiler.hpp deleted file mode 100644 index 93fe835..0000000 --- a/include/matador/sql/query_compiler.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef QUERY_QUERY_COMPILER_HPP -#define QUERY_QUERY_COMPILER_HPP - -#include "matador/sql/query_part_visitor.hpp" -#include "matador/sql/query_parts.hpp" -#include "matador/sql/query_context.hpp" - -#include -#include - -namespace matador::sql { - -class dialect; - -struct query_data; - -class query_compiler : public query_part_visitor -{ -public: - explicit query_compiler(const sql::dialect& d); - - query_context compile(const query_data *data); - -private: - void visit(query_select_part &select_part) override; - void visit(query_from_part &from_part) override; - void visit(query_join_part &join_part) override; - void visit(query_on_part &on_part) override; - void visit(query_where_part &where_part) override; - void visit(query_group_by_part &group_by_part) override; - void visit(query_order_by_part &order_by_part) override; - void visit(query_order_by_asc_part &order_by_asc_part) override; - void visit(query_order_by_desc_part &order_by_desc_part) override; - void visit(query_offset_part &offset_part) override; - void visit(query_limit_part &limit_part) override; - void visit(query_insert_part &insert_part) override; - void visit(query_into_part &into_part) override; - void visit(query_values_part &values_part) override; - - void visit(query_update_part &update_part) override; - void visit(query_set_part &set_part) override; - - void visit(query_delete_part &delete_part) override; - void visit(query_delete_from_part &delete_from_part) override; - - void visit(query_create_part &create_part) override; - void visit(query_create_table_part &create_table_part) override; - - void visit(query_drop_part &drop_part) override; - void visit(query_drop_table_part &drop_table_part) override; - -private: - const sql::dialect &dialect_; - query_context query_; -}; - -} - -#endif //QUERY_QUERY_COMPILER_HPP diff --git a/include/matador/sql/query_context.hpp b/include/matador/sql/query_context.hpp index f51fcbf..2ac1a3a 100644 --- a/include/matador/sql/query_context.hpp +++ b/include/matador/sql/query_context.hpp @@ -1,21 +1,39 @@ -#ifndef QUERY_QUERY_CONTEXT_HPP -#define QUERY_QUERY_CONTEXT_HPP +#ifndef QUERY_QUERY_DATA_HPP +#define QUERY_QUERY_DATA_HPP #include "matador/sql/column_definition.hpp" #include "matador/sql/table.hpp" +#include "matador/utils/types.hpp" + namespace matador::sql { +enum class sql_command { + SQL_CMD_UNKNOWN, + SQL_CMD_CREATE, + SQL_CMD_UPDATE, + SQL_CMD_INSERT, + SQL_CMD_DELETE, + SQL_CMD_SELECT, + SQL_CMD_DROP, + SQL_CMD_ALTER + }; + struct query_context { std::string sql; + sql_command command{}; std::string command_name; sql::table table{""}; std::vector prototype; std::vector result_vars; std::vector bind_vars; + std::vector bind_types; + + std::unordered_map column_aliases; + std::unordered_map table_aliases; }; } -#endif //QUERY_QUERY_CONTEXT_HPP +#endif //QUERY_QUERY_DATA_HPP diff --git a/include/matador/sql/query_data.hpp b/include/matador/sql/query_data.hpp deleted file mode 100644 index 20cadc8..0000000 --- a/include/matador/sql/query_data.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef QUERY_QUERY_DATA_HPP -#define QUERY_QUERY_DATA_HPP - -#include "matador/sql/query_part.hpp" -#include "matador/sql/table.hpp" - -#include -#include - -namespace matador::sql { - -class query_part; - -struct query_data -{ -// SqlCommands command; - std::vector> parts; - std::vector columns; -}; - -} - -#endif //QUERY_QUERY_DATA_HPP diff --git a/include/matador/sql/query_helper.hpp b/include/matador/sql/query_helper.hpp deleted file mode 100644 index 0aaf0f9..0000000 --- a/include/matador/sql/query_helper.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef QUERY_QUERY_HELPER_HPP -#define QUERY_QUERY_HELPER_HPP - -#include "matador/utils/macro_map.hpp" - -#include "matador/sql/table.hpp" -#include "matador/sql/column.hpp" - -#include -#include - -#define FIELD(x) const sql::column x{*this, #x, ""}; - -#define QUERY_HELPER(C, ...) \ -namespace matador::qh { \ -namespace internal { \ -struct C##_query : sql::table { \ -C##_query() : table(#C) {} \ -MAP(FIELD, __VA_ARGS__) \ -}; } \ -static const internal:: C##_query C; \ -} - -#endif //QUERY_QUERY_HELPER_HPP diff --git a/include/matador/sql/query_intermediates.hpp b/include/matador/sql/query_intermediates.hpp deleted file mode 100644 index 21f2b5d..0000000 --- a/include/matador/sql/query_intermediates.hpp +++ /dev/null @@ -1,344 +0,0 @@ -#ifndef QUERY_QUERY_INTERMEDIATES_HPP -#define QUERY_QUERY_INTERMEDIATES_HPP - -#include "matador/sql/column_definition.hpp" -#include "matador/sql/column_definition_generator.hpp" -#include "matador/sql/column_generator.hpp" -#include "matador/sql/key_value_generator.hpp" -#include "matador/sql/key_value_pair.hpp" -#include "matador/sql/placeholder_generator.hpp" -#include "matador/sql/query_result.hpp" -#include "matador/sql/query_data.hpp" -#include "matador/sql/record.hpp" -#include "matador/sql/statement.hpp" -#include "matador/sql/schema.hpp" -#include "matador/sql/value_extractor.hpp" - -#include - -namespace matador::sql { - -class basic_condition; -class connection; - -class basic_query_intermediate -{ -public: - explicit basic_query_intermediate(connection &db, const sql::schema &schema); - -protected: - connection &connection_; - const sql::schema &schema_; -}; - -class query_intermediate : public basic_query_intermediate -{ -public: - query_intermediate(connection &db, const sql::schema &schema, const std::shared_ptr &data); - -protected: - std::shared_ptr data_; -}; - -class query_execute : public query_intermediate -{ -public: - using query_intermediate::query_intermediate; - - size_t execute(); - statement prepare(); - [[nodiscard]] query_context build() const; -}; - -class query_select : public query_intermediate -{ -protected: - using query_intermediate::query_intermediate; - -public: - template < class Type > - query_result fetch_all() - { - return query_result(fetch()); - } - query_result fetch_all(); - - template < class Type > - std::unique_ptr fetch_one() - { - auto result = query_result(fetch()); - auto first = result.begin(); - if (first == result.end()) { - return nullptr; - } - - return std::unique_ptr{first.release()}; - } - std::optional fetch_one(); - - template - std::optional fetch_value() - { - const auto result = fetch_one(); - if (result.has_value()) { - return result.value().at(0).as().value(); - } - return std::nullopt; - } - - statement prepare(); - - [[nodiscard]] query_context build() const; - -private: - std::unique_ptr fetch(); -}; - -class query_offset_intermediate; - -class query_limit_intermediate : public query_select -{ -public: - using query_select::query_select; - - query_offset_intermediate offset(size_t offset); -}; - -class query_offset_intermediate : public query_select -{ -public: - using query_select::query_select; - - query_limit_intermediate limit(size_t limit); -}; - -class query_order_direction_intermediate : public query_select -{ -public: - using query_select::query_select; - - query_limit_intermediate limit(size_t limit); -}; - -class query_order_by_intermediate; - -class query_group_by_intermediate : public query_select -{ -public: - using query_select::query_select; - - query_order_by_intermediate order_by(const column &col); -}; - -class query_order_by_intermediate : public query_intermediate -{ -public: - using query_intermediate::query_intermediate; - - query_order_direction_intermediate asc(); - query_order_direction_intermediate desc(); -}; - -class query_where_intermediate : public query_select -{ -public: - using query_select::query_select; - - query_group_by_intermediate group_by(const column &col); - query_order_by_intermediate order_by(const column &col); -}; - -class query_join_intermediate; - -struct join_data -{ - table join_table; - std::unique_ptr condition; -}; - -class query_from_intermediate : public query_select -{ -public: - using query_select::query_select; - - query_join_intermediate join_left(const table &t); - query_from_intermediate join_left(join_data &data); - query_from_intermediate join_left(std::vector &data_vector); - - template - query_where_intermediate where(const Condition &cond) - { - return where_clause(std::make_unique(std::move(cond))); - } - query_where_intermediate where(std::unique_ptr &&cond) - { - return where_clause(std::move(cond)); - } - query_group_by_intermediate group_by(const column &col); - query_order_by_intermediate order_by(const column &col); - -private: - query_where_intermediate where_clause(std::unique_ptr &&cond); -}; - -using query_on_intermediate = query_from_intermediate; - -class query_join_intermediate : public query_intermediate -{ -public: - using query_intermediate::query_intermediate; - - template - query_on_intermediate on(const Condition &cond) - { - return on_clause(std::make_unique(std::move(cond))); - } - query_on_intermediate on(std::unique_ptr &&cond) - { - return on_clause(std::move(cond)); - } - -private: - query_on_intermediate on_clause(std::unique_ptr &&cond); -}; - -class query_start_intermediate : public basic_query_intermediate -{ -public: - explicit query_start_intermediate(connection &db, const sql::schema &schema); - -protected: - std::shared_ptr data_ { std::make_shared() }; -}; - -class query_select_intermediate : public query_start_intermediate -{ -public: - query_select_intermediate(connection &db, const sql::schema &schema, const std::vector& columns); - - query_from_intermediate from(const table& t); -}; - -template < class Type > -std::vector as_placeholder(const Type &obj) -{ - placeholder_generator generator; - matador::utils::access::process(generator, obj); - - return generator.placeholder_values; -} - -class query_into_intermediate : public query_intermediate -{ -public: - using query_intermediate::query_intermediate; - - query_execute values(std::initializer_list values); - query_execute values(std::vector &&values); - template - query_execute values() - { - Type obj; - return values(std::move(as_placeholder(obj))); - } - template - query_execute values(const Type &obj) - { - return values(std::move(value_extractor::extract(obj))); - } -}; - -class query_create_intermediate : public query_start_intermediate -{ -public: - explicit query_create_intermediate(connection &db, const sql::schema &schema); - - query_execute table(const sql::table &table, std::initializer_list columns); - query_execute table(const sql::table &table, const std::vector &columns); - template - query_execute table(const sql::table &table) - { - return this->table(table, column_definition_generator::generate(schema_)); - } -}; - -class query_drop_intermediate : query_start_intermediate -{ -public: - explicit query_drop_intermediate(connection &db, const sql::schema &schema); - - query_execute table(const sql::table &table); -}; - -class query_insert_intermediate : public query_start_intermediate -{ -public: - explicit query_insert_intermediate(connection &db, const sql::schema &schema); - - query_into_intermediate into(const sql::table &table, std::initializer_list column_names); - query_into_intermediate into(const sql::table &table, std::vector &&column_names); - query_into_intermediate into(const sql::table &table); -}; - -class query_execute_where_intermediate : public query_execute -{ -public: - using query_execute::query_execute; - - query_order_by_intermediate order_by(const column &col); -}; - -class query_set_intermediate : public query_execute -{ -public: - using query_execute::query_execute; - - template - query_execute_where_intermediate where(const Condition &cond) - { - return where_clause(std::make_unique(std::move(cond))); - } - -private: - query_execute_where_intermediate where_clause(std::unique_ptr &&cond); -}; - -class query_update_intermediate : public query_start_intermediate -{ -public: - query_update_intermediate(connection &db, const sql::schema &schema, const sql::table& table); - - query_set_intermediate set(std::initializer_list columns); - query_set_intermediate set(std::vector &&columns); - template - query_set_intermediate set(const Type &obj) - { - return set(key_value_generator::generate(obj)); - } -}; - -class query_delete_from_intermediate : public query_execute -{ -public: - using query_execute::query_execute; - - template - query_execute_where_intermediate where(const Condition &cond) - { - return where_clause(std::make_unique(std::move(cond))); - } - -private: - query_execute_where_intermediate where_clause(std::unique_ptr &&cond); -}; - -class query_delete_intermediate : public query_start_intermediate -{ -public: - explicit query_delete_intermediate(connection &db, const sql::schema &schema); - - query_delete_from_intermediate from(const sql::table &table); -}; - -} -#endif //QUERY_QUERY_INTERMEDIATES_HPP diff --git a/include/matador/sql/query_part_visitor.hpp b/include/matador/sql/query_part_visitor.hpp deleted file mode 100644 index bb42cf4..0000000 --- a/include/matador/sql/query_part_visitor.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef QUERY_QUERY_PART_VISITOR_HPP -#define QUERY_QUERY_PART_VISITOR_HPP - -namespace matador::sql { - -class query_select_part; -class query_from_part; -class query_join_part; -class query_on_part; -class query_where_part; -class query_group_by_part; -class query_order_by_part; -class query_order_by_asc_part; -class query_order_by_desc_part; -class query_offset_part; -class query_limit_part; -class query_insert_part; -class query_into_part; -class query_values_part; -class query_update_part; -class query_set_part; -class query_delete_part; -class query_delete_from_part; -class query_create_part; -class query_create_table_part; -class query_drop_part; -class query_drop_table_part; - -class query_part_visitor -{ -public: - virtual ~query_part_visitor() = default; - - virtual void visit(query_select_part &select_part) = 0; - virtual void visit(query_from_part &from_part) = 0; - virtual void visit(query_join_part &join_part) = 0; - virtual void visit(query_on_part &on_part) = 0; - virtual void visit(query_where_part &where_part) = 0; - virtual void visit(query_group_by_part &group_by_part) = 0; - virtual void visit(query_order_by_part &order_by_part) = 0; - virtual void visit(query_order_by_asc_part &order_by_asc_part) = 0; - virtual void visit(query_order_by_desc_part &order_by_desc_part) = 0; - virtual void visit(query_offset_part &offset_part) = 0; - virtual void visit(query_limit_part &limit_part) = 0; - - virtual void visit(query_insert_part &insert_part) = 0; - virtual void visit(query_into_part &into_part) = 0; - virtual void visit(query_values_part &values_part) = 0; - - virtual void visit(query_update_part &update_part) = 0; - virtual void visit(query_set_part &set_part) = 0; - - virtual void visit(query_delete_part &delete_part) = 0; - virtual void visit(query_delete_from_part &delete_from_part) = 0; - - virtual void visit(query_create_part &create_part) = 0; - virtual void visit(query_create_table_part &create_table_part) = 0; - - virtual void visit(query_drop_part &drop_part) = 0; - virtual void visit(query_drop_table_part &drop_table_part) = 0; -}; - -} - -#endif //QUERY_QUERY_PART_VISITOR_HPP diff --git a/include/matador/sql/query_result.hpp b/include/matador/sql/query_result.hpp index 22df5f3..8942feb 100644 --- a/include/matador/sql/query_result.hpp +++ b/include/matador/sql/query_result.hpp @@ -1,7 +1,9 @@ #ifndef QUERY_QUERY_RESULT_HPP #define QUERY_QUERY_RESULT_HPP -#include "matador/sql/query_result_impl.hpp" +#include "matador/sql/column_definition.hpp" + +#include "matador/sql/internal/query_result_impl.hpp" #include #include @@ -20,7 +22,7 @@ public: using iterator_category = std::forward_iterator_tag; using value_type = Type; using difference_type = std::ptrdiff_t; - using self = query_result_iterator; /**< Shortcut for this class. */ + using self = query_result_iterator; /**< Shortcut for this class. */ using pointer = value_type*; /**< Shortcut for the pointer type. */ using reference = value_type&; /**< Shortcut for the reference type */ @@ -78,7 +80,7 @@ public: obj_.reset(); } - return std::move(tmp); + return tmp; } pointer operator->() @@ -119,43 +121,43 @@ record* create_prototype(const std::vector &prototype } -template < typename Type > -class query_result -{ +template +class query_result final { public: using iterator = query_result_iterator; using creator_func = std::function; public: - explicit query_result(std::unique_ptr impl) + explicit query_result(std::unique_ptr &&impl) : impl_(std::move(impl)) {} - query_result(std::unique_ptr impl, std::vector record_prototype) - : record_prototype_(std::move(record_prototype)) - , impl_(std::move(impl)) {} - iterator begin() { return std::move(++iterator(this)); } iterator end() { return {}; } private: friend class query_result_iterator; - Type* create() { return detail::create_prototype(record_prototype_); } + Type* create(); + void bind(const Type& obj); + bool fetch(Type& obj); - void bind(const Type &obj) - { - impl_->bind(obj); - } - - bool fetch(Type &obj) - { - return impl_->fetch(obj); - } - -private: - std::vector record_prototype_; +protected: std::unique_ptr impl_; }; +template +Type *query_result::create() { + return detail::create_prototype(impl_->prototype()); } +template +void query_result::bind(const Type &obj) { + impl_->bind(obj); +} + +template +bool query_result::fetch(Type &obj) { + return impl_->fetch(obj); +} + +} // namespace matador::sql #endif //QUERY_QUERY_RESULT_HPP diff --git a/include/matador/sql/query_result_reader.hpp b/include/matador/sql/query_result_reader.hpp deleted file mode 100644 index a18bf26..0000000 --- a/include/matador/sql/query_result_reader.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef QUERY_QUERY_RESULT_READER_HPP -#define QUERY_QUERY_RESULT_READER_HPP - -#include "matador/sql/any_type.hpp" -#include "matador/sql/data_type_traits.hpp" - -namespace matador::sql { - -class value; - -class query_result_reader -{ -public: - virtual ~query_result_reader() = default; - - [[nodiscard]] virtual size_t column_count() const = 0; - [[nodiscard]] virtual const char* column(size_t index) const = 0; - [[nodiscard]] virtual bool fetch() = 0; - - virtual void read_value(const char *id, size_t index, char &value); - virtual void read_value(const char *id, size_t index, short &value); - virtual void read_value(const char *id, size_t index, int &value); - virtual void read_value(const char *id, size_t index, long &value); - virtual void read_value(const char *id, size_t index, long long &value); - virtual void read_value(const char *id, size_t index, unsigned char &value); - virtual void read_value(const char *id, size_t index, unsigned short &value); - virtual void read_value(const char *id, size_t index, unsigned int &value); - virtual void read_value(const char *id, size_t index, unsigned long &value); - virtual void read_value(const char *id, size_t index, unsigned long long &value); - virtual void read_value(const char *id, size_t index, bool &value); - virtual void read_value(const char *id, size_t index, float &value); - virtual void read_value(const char *id, size_t index, double &value); -// virtual void read_value(const char *id, size_t index, matador::time &value); -// virtual void read_value(const char *id, size_t index, matador::date &value); - virtual void read_value(const char *id, size_t index, char *value, size_t s); - virtual void read_value(const char *id, size_t index, std::string &value); - virtual void read_value(const char *id, size_t index, std::string &value, size_t s); - virtual void read_value(const char *id, size_t index, utils::blob &value); - virtual void read_value(const char *id, size_t index, value &val, size_t size); -}; - -} -#endif //QUERY_QUERY_RESULT_READER_HPP diff --git a/include/matador/sql/record.hpp b/include/matador/sql/record.hpp index 47d3e17..1dc3bdc 100644 --- a/include/matador/sql/record.hpp +++ b/include/matador/sql/record.hpp @@ -47,6 +47,16 @@ public: [[nodiscard]] const field& at(const column &col) const; [[nodiscard]] const field& at(size_t index) const; + template + std::optional at(const column &col) const + { + return at(col).as(); + } + template + std::optional at(size_t index) const + { + return at(index).as(); + } iterator find(const std::string &column_name); [[nodiscard]] const_iterator find(const std::string &column_name) const; diff --git a/include/matador/sql/result_parameter_binder.hpp b/include/matador/sql/result_parameter_binder.hpp deleted file mode 100644 index f88e5fc..0000000 --- a/include/matador/sql/result_parameter_binder.hpp +++ /dev/null @@ -1,133 +0,0 @@ -#ifndef QUERY_RESULT_PARAMETER_BINDER_HPP -#define QUERY_RESULT_PARAMETER_BINDER_HPP - -#include "matador/utils/access.hpp" -#include "matador/utils/cascade_type.hpp" -#include "matador/utils/field_attributes.hpp" - -#include "matador/sql/any_type.hpp" -#include "matador/sql/data_type_traits.hpp" - -#include - -namespace matador::sql { - -class result_parameter_binder; - -namespace detail { -class fk_result_binder -{ -public: - explicit fk_result_binder(result_parameter_binder &result_binder); - - template - void bind_result(Type &obj, size_t column_index) - { - column_index_ = column_index; - utils::access::process(*this, obj); - } - - template - void on_primary_key(const char *id, ValueType &value, typename std::enable_if::value && !std::is_same::value>::type* = 0); - void on_primary_key(const char *id, std::string &value, size_t size); - void on_revision(const char * /*id*/, unsigned long long &/*rev*/) {} - - template < class Type > - void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} - template < class Pointer > - void on_belongs_to(const char * /*id*/, Pointer &/*x*/, utils::cascade_type) {} - template < class Pointer > - void on_has_one(const char * /*id*/, Pointer &/*x*/, utils::cascade_type) {} - - template - void on_has_many(const char *, ContainerType &, const char *, const char *, utils::cascade_type) {} - template - void on_has_many(const char *, ContainerType &, utils::cascade_type) {} - -private: - size_t column_index_{}; - result_parameter_binder &result_binder_; -}; - -} - -class result_parameter_binder -{ -public: - template - void on_primary_key(const char * /*id*/, ValueType &value, typename std::enable_if::value && !std::is_same::value>::type* = 0) - { - data_type_traits::bind_result_value(*this, column_index_++, value); - } - void on_primary_key(const char *id, std::string &value, size_t size); - void on_revision(const char *id, unsigned long long &rev); - - template < class Type > - void on_attribute(const char * /*id*/, Type &x, const utils::field_attributes &/*attr*/ = utils::null_attributes) - { - data_type_traits::bind_result_value(*this, column_index_++, x); - } - void on_attribute(const char *id, char *value, const utils::field_attributes &attr = utils::null_attributes); - void on_attribute(const char *id, std::string &value, const utils::field_attributes &attr = utils::null_attributes); - void on_attribute(const char *id, any_type &value, data_type_t type, const utils::field_attributes &attr = utils::null_attributes); - - template < class Pointer > - void on_belongs_to(const char * /*id*/, Pointer &x, utils::cascade_type) - { - if (!x.get()) { - x.reset(new typename Pointer::value_type); - } - fk_result_binder_.bind_result(*x, column_index_++); - } - template < class Pointer > - void on_has_one(const char * /*id*/, Pointer &x, utils::cascade_type) - { - if (!x.get()) { - x.reset(new typename Pointer::value_type); - } - fk_result_binder_.bind_result(*x, column_index_++); - } - - template - void on_has_many(const char *, ContainerType &, const char *, const char *, utils::cascade_type) {} - template - void on_has_many(const char *, ContainerType &, utils::cascade_type) {} - - virtual void bind_result_value(size_t index, char &value) {} - virtual void bind_result_value(size_t index, short &value) {} - virtual void bind_result_value(size_t index, int &value) {} - virtual void bind_result_value(size_t index, long &value) {} - virtual void bind_result_value(size_t index, long long &value) {} - virtual void bind_result_value(size_t index, unsigned char &value) {} - virtual void bind_result_value(size_t index, unsigned short &value) {} - virtual void bind_result_value(size_t index, unsigned int &value) {} - virtual void bind_result_value(size_t index, unsigned long &value) {} - virtual void bind_result_value(size_t index, unsigned long long &value) {} - virtual void bind_result_value(size_t index, bool &value) {} - virtual void bind_result_value(size_t index, float &value) {} - virtual void bind_result_value(size_t index, double &value) {} -// virtual void bind_result_value(size_t index, matador::time &value) {} -// virtual void bind_result_value(size_t index, matador::date &value) {} - virtual void bind_result_value(size_t index, char *value, size_t s) {} - virtual void bind_result_value(size_t index, std::string &value) {} - virtual void bind_result_value(size_t index, std::string &value, size_t s) {} - virtual void bind_result_value(size_t index, any_type &value, data_type_t type, size_t size) {} - -private: - size_t column_index_{}; - detail::fk_result_binder fk_result_binder_; -}; - -namespace detail { - -template -void fk_result_binder::on_primary_key(const char * /*id*/, ValueType &value, typename std::enable_if::value && !std::is_same::value>::type *) -{ - data_type_traits::bind_result_value(result_binder_, column_index_++, value); -} - - -} -} - -#endif //QUERY_RESULT_PARAMETER_BINDER_HPP diff --git a/include/matador/sql/schema.hpp b/include/matador/sql/schema.hpp deleted file mode 100644 index 1c85c55..0000000 --- a/include/matador/sql/schema.hpp +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef QUERY_SCHEMA_HPP -#define QUERY_SCHEMA_HPP - -#include "matador/sql/column_definition_generator.hpp" -#include "matador/sql/table_definition.hpp" - -#include -#include -#include -#include - -namespace matador::sql { - -class connection; - -struct table_info -{ - std::string name; - table_definition prototype; -}; - -class schema -{ -public: - using repository = std::unordered_map; - using repository_by_name = std::unordered_map>; - using iterator = repository::iterator; - using const_iterator = repository::const_iterator; - - schema() = delete; - explicit schema(std::string name); - schema(const schema&) = delete; - schema& operator=(const schema&) = delete; - schema(schema&&) noexcept = default; - schema& operator=(schema&&) noexcept = default; - - [[nodiscard]] std::string name() const; - - void create(connection &c); - - template - const table_info& attach(const std::string &table_name) - { - return attach(std::type_index(typeid(Type)), table_info{table_name, table_definition{column_definition_generator::generate(*this)}}); - } - - const table_info& attach(std::type_index ti, const table_info& table); - - template - [[nodiscard]] std::optional info() const - { - return info(std::type_index(typeid(Type))); - } - - [[nodiscard]] std::optional info(std::type_index ti) const; - [[nodiscard]] std::optional info(const std::string &name) const; - - template - [[nodiscard]] std::pair reference() const - { - return reference(std::type_index(typeid(Type))); - } - - [[nodiscard]] std::pair reference(const std::type_index &ti) const; - - template - [[nodiscard]] bool exists() const - { - return exists(std::type_index(typeid(Type))); - } - - [[nodiscard]] bool exists(const std::type_index &ti) const; - - iterator begin(); - [[nodiscard]] const_iterator begin() const; - iterator end(); - [[nodiscard]] const_iterator end() const; - - [[nodiscard]] bool empty() const; - -private: - std::string name_; - repository repository_; - repository_by_name repository_by_name_; -}; - -} - -#endif //QUERY_SCHEMA_HPP diff --git a/include/matador/sql/session.hpp b/include/matador/sql/session.hpp deleted file mode 100644 index 5dfecb0..0000000 --- a/include/matador/sql/session.hpp +++ /dev/null @@ -1,170 +0,0 @@ -#ifndef QUERY_SESSION_HPP -#define QUERY_SESSION_HPP - -#include "matador/sql/connection.hpp" -#include "matador/sql/connection_pool.hpp" -#include "matador/sql/entity.hpp" -#include "matador/sql/entity_query_builder.hpp" -#include "matador/sql/statement.hpp" -#include "matador/sql/schema.hpp" - -#include - -namespace matador::sql { - -class dialect; - -enum class session_error { - Ok = 0, - NoConnectionAvailable, - UnknownType, - FailedToBuildQuery, - FailedToFindObject -}; - -class session -{ -public: - explicit session(connection_pool &pool); - - template - void attach(const std::string &table_name); - - void create_schema(); - - template - entity insert(Type *obj); - - template< class Type, typename... Args > - entity insert(Args&&... args) { - return insert(new Type(std::forward(args)...)); - } - - template - utils::result, session_error> find(const PrimaryKeyType &pk) { - auto c = pool_.acquire(); - if (!c.valid()) { - return utils::error(session_error::NoConnectionAvailable); - } - auto info = schema_->info(); - if (!info) { - return utils::error(session_error::UnknownType); - } - - entity_query_builder eqb(*schema_); - auto data = eqb.build(pk); - if (!data.is_ok()) { - return utils::error(session_error::FailedToBuildQuery); - } - - auto obj = build_select_query(c, data.release()).template fetch_one(); - - if (!obj) { - return utils::error(session_error::FailedToFindObject); - } - return utils::ok(entity{ obj.release() }); - } - - template - utils::result, session_error> find() { - auto c = pool_.acquire(); - if (!c.valid()) { - return utils::error(session_error::NoConnectionAvailable); - } - auto info = schema_->info(); - if (!info) { - return utils::error(session_error::UnknownType); - } - - entity_query_builder eqb(*schema_); - auto data = eqb.build(); - if (!data.is_ok()) { - return utils::error(session_error::FailedToBuildQuery); - } - - return utils::ok(build_select_query(c, data.release()).template fetch_all()); - } - - template - utils::result select() { - auto c = pool_.acquire(); - if (!c.valid()) { - return utils::error(session_error::NoConnectionAvailable); - } - auto info = schema_->info(); - if (!info) { - return utils::error(session_error::UnknownType); - } - - entity_query_builder eqb(*schema_); - auto data = eqb.build(); - if (!data.is_ok()) { - return utils::error(session_error::FailedToBuildQuery); - } - - return utils::ok(build_select_query(c, data.release()).template fetch_all()); - } - - template - void drop_table(); - void drop_table(const std::string &table_name); - - [[nodiscard]] query_result fetch(const query_context &q) const; -// [[nodiscard]] query_result fetch(const std::string &sql) const; - [[nodiscard]] size_t execute(const std::string &sql) const; - statement prepare(query_context q) const; - - std::vector describe_table(const std::string &table_name) const; - bool table_exists(const std::string &table_name) const; - - const class dialect& dialect() const; - -private: - friend class query_select; - - [[nodiscard]] std::unique_ptr fetch(const std::string &sql) const; - - query_select build_select_query(connection_ptr &conn, entity_query_data &&data) const; - -private: - connection_pool &pool_; - const class dialect &dialect_; - - std::unique_ptr schema_; - mutable std::unordered_map prototypes_; -}; - -template -void session::attach(const std::string &table_name) -{ - schema_->attach(table_name); -} - -template -entity session::insert(Type *obj) -{ - auto c = pool_.acquire(); - auto info = schema_->info(); - if (!info) { - return {}; - } - c->query(*schema_) - .insert() - .into(info->name, column_generator::generate(*schema_, true)) - .values(*obj) - .execute(); - - return entity{obj}; -} - -template -void session::drop_table() -{ - auto info = schema_->info(); - if (info) { - return drop_table(info.name); - } - -} -} -#endif //QUERY_SESSION_HPP diff --git a/include/matador/sql/statement.hpp b/include/matador/sql/statement.hpp index 4c0f641..a352ba1 100644 --- a/include/matador/sql/statement.hpp +++ b/include/matador/sql/statement.hpp @@ -1,57 +1,174 @@ #ifndef QUERY_STATEMENT_HPP #define QUERY_STATEMENT_HPP -#include "matador/sql/object_parameter_binder.hpp" +#include "matador/sql/abstract_sql_logger.hpp" #include "matador/sql/query_result.hpp" -#include "matador/sql/statement_impl.hpp" -#include "matador/utils/logger.hpp" +#include "matador/sql/interface/statement_impl.hpp" + +#include "matador/utils/error.hpp" +#include "matador/utils/result.hpp" #include namespace matador::sql { +namespace detail { +template +class identifier_binder; +} -class statement -{ +class statement_impl; + +class statement { public: - explicit statement(std::unique_ptr impl, const utils::logger &logger); + /** + * Creates a statement initialized from the + * given statement implementation object holding + * the implementation for the selected database + * + * @param impl The statement implementation object + * @param logger The logger handler to write sql log messages to + */ + explicit statement(std::unique_ptr impl, + const std::shared_ptr &logger = std::make_shared()); + /** + * Copy move constructor for statement + * + * @param x The statement to move from + */ + statement(statement &&x) noexcept + : statement_(std::move(x.statement_)) + , logger_(std::move(x.logger_)) { + } - template < typename Type > - statement& bind(size_t pos, const Type &value) - { - statement_->bind(pos, value); + /** + * Assignment move constructor for statement + * + * @param x The statement to move from + * @return Reference to this + */ + statement &operator=(statement &&x) noexcept { + statement_ = std::move(x.statement_); + logger_ = std::move(x.logger_); return *this; } - statement& bind(size_t pos, const char *value); - - statement& bind(size_t pos, std::string &val, size_t size); - - template < class Type > - statement& bind(const Type &obj) - { - object_binder_.reset(); - matador::utils::access::process(object_binder_, obj); - return *this; - } - - size_t execute(); - + statement &bind(size_t pos, const char *value); + statement &bind(size_t pos, std::string &val, size_t size); + /** + * Bind an object to the statement starting + * at the given position index. + * + * @param obj The object to bind + * @return The next index to bind + */ template - query_result fetch() - { - logger_.info(statement_->query_.sql); - return query_result(statement_->fetch()); - } - query_result fetch(); + statement &bind(const Type &obj); + template + statement &bind(size_t pos, Type &value); - void reset(); + /** + * Executes the prepared statement and returns + * the number of affected rows. + * + * @return The number of affected rows + */ + [[nodiscard]] utils::result execute() const; + + /** + * Fetches the result of the prepared + * statement. If prepared statement was not + * a SELECT statement an empty query result set + * is returned. + * + * @tparam Type Type of the fetched result + * @return The query result set + */ + template + utils::result, utils::error> fetch(); + /** + * Fetches the result of the prepared + * statement. The type is record representing an + * unknown variable type. + * If prepared statement was not + * a SELECT statement an empty query result set + * is returned. + * + * @return The query result set + */ + [[nodiscard]] utils::result, utils::error> fetch() const; + /** + * Fetches the first result of a prepared statement. + * If prepared statement is empty or not + * a SELECT statement a nullptr is returned. + * + * @tparam Type Type of the fetched result + * @return The query result set + */ + template + utils::result, utils::error> fetch_one(); + /** + * Fetches the first result of a prepared statement. + * The type is record representing an unknown variable type. + * If prepared statement is empty or not + * a SELECT statement a nullptr is returned. + * + * @return The query result set + */ + [[nodiscard]] utils::result, utils::error> fetch_one() const; + + /** + * Resets the prepared statement to + * reuse it. + */ + void reset() const; + +private: + template + friend class detail::identifier_binder; private: std::unique_ptr statement_; - const utils::logger &logger_; - object_parameter_binder object_binder_; + std::shared_ptr logger_; }; + +template +statement &statement::bind(size_t pos, Type &value) { + statement_->bind(pos, value); + return *this; +} + +template +statement &statement::bind(const Type &obj) { + statement_->bind_object(obj); + return *this; +} + +template +utils::result, utils::error> statement::fetch() { + return statement_->fetch().and_then([](std::unique_ptr &&value) { + return utils::ok(query_result(std::forward(value))); + }); +// if (!result.is_ok()) { +// return utils::error(result.err()); +// } +// return query_result(result.release()); +} + +template +utils::result, utils::error> statement::fetch_one() { + auto result = statement_->fetch(); + if (!result.is_ok()) { + return utils::failure(result.err()); + } + auto records = query_result(result.release()); + auto first = records.begin(); + if (first == records.end()) { + return utils::ok(std::unique_ptr{nullptr}); + } + + return utils::ok(std::unique_ptr{first.release()}); +} } #endif //QUERY_STATEMENT_HPP diff --git a/include/matador/sql/statement_cache.hpp b/include/matador/sql/statement_cache.hpp deleted file mode 100644 index 1cda0ba..0000000 --- a/include/matador/sql/statement_cache.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef QUERY_STATEMENT_CACHE_HPP -#define QUERY_STATEMENT_CACHE_HPP - -#include "matador/sql/statement.hpp" - -#include -#include -#include -#include - -namespace matador::sql { - -class connection; - -struct cache_info -{ - statement statement_; -// std::unique_ptr statement_; - size_t connection_id_; -}; - -class statement_cache -{ -public: - statement& acquire(query_context &&context, const connection &conn); - void release(const statement &stmt); - -private: - mutable std::mutex mutex_; - size_t max_cache_size_{256}; - std::hash hash_; - using statement_map = std::unordered_map; - statement_map statement_map_; -}; - -} -#endif //QUERY_STATEMENT_CACHE_HPP diff --git a/include/matador/sql/statement_impl.hpp b/include/matador/sql/statement_impl.hpp deleted file mode 100644 index bf1de73..0000000 --- a/include/matador/sql/statement_impl.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef QUERY_STATEMENT_IMPL_HPP -#define QUERY_STATEMENT_IMPL_HPP - -#include "matador/sql/query_context.hpp" -#include "matador/sql/query_result_impl.hpp" -#include "matador/sql/parameter_binder.hpp" -#include "matador/sql/data_type_traits.hpp" - -#include - -namespace matador::sql { - -class statement_impl -{ -protected: - explicit statement_impl(query_context query); - -public: - virtual ~statement_impl() = default; - - virtual size_t execute() = 0; - virtual std::unique_ptr fetch() = 0; - - template < class Type > - void bind(size_t pos, Type &val) - { - data_type_traits::bind_value(binder(), pos, val); - } - - void bind(size_t pos, const char *value, size_t size); - void bind(size_t pos, std::string &val, size_t size); - - virtual void reset() = 0; - -protected: - virtual parameter_binder& binder() = 0; - -protected: - friend class statement; - - query_context query_; -}; - -} - -#endif //QUERY_STATEMENT_IMPL_HPP diff --git a/include/matador/sql/table.hpp b/include/matador/sql/table.hpp index d7ce0c4..45f2183 100644 --- a/include/matador/sql/table.hpp +++ b/include/matador/sql/table.hpp @@ -3,32 +3,32 @@ #include "matador/sql/column.hpp" -#include #include #include namespace matador::sql { +struct column; + struct table { - table(const char *name, std::string as = "") // NOLINT(*-explicit-constructor) - : name(name), alias(std::move(as)) {} - table(std::string name, std::string as = "") // NOLINT(*-explicit-constructor) - : name(std::move(name)) - , alias(std::move(as)) {} + table() = default; + table(const char *name); // NOLINT(*-explicit-constructor) + table(std::string name); // NOLINT(*-explicit-constructor) + table(const char *name, std::string as); // NOLINT(*-explicit-constructor) + table(std::string name, std::string as); // NOLINT(*-explicit-constructor) table(std::string name, std::string as, const std::vector &columns) : name(std::move(name)) , alias(std::move(as)) , columns(columns) {} - table& as(const std::string &a) { - alias = a; - return *this; - } + table& as(const std::string &a); - [[nodiscard]] table as(const std::string &a) const { - return { name, a, columns }; - } + [[nodiscard]] bool operator==(const table &x) const; + + [[nodiscard]] table as(const std::string &a) const; + + [[nodiscard]] bool has_alias() const; std::string name; std::string alias; @@ -36,6 +36,8 @@ struct table std::vector columns; }; +table operator "" _tab(const char *name, size_t len); + } #endif //QUERY_TABLE_HPP diff --git a/include/matador/sql/table_definition.hpp b/include/matador/sql/table_definition.hpp deleted file mode 100644 index 14077bd..0000000 --- a/include/matador/sql/table_definition.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef QUERY_TABLE_DEFINITION_HPP -#define QUERY_TABLE_DEFINITION_HPP - -#include "matador/sql/column.hpp" -#include "matador/sql/column_definition.hpp" - -#include - -namespace matador::sql { - -class table_definition final -{ -private: - using column_by_index = std::vector; - using column_index_pair = std::pair, column_by_index::difference_type>; - using column_by_name_map = std::unordered_map; - -public: - using iterator = column_by_index::iterator; - using const_iterator = column_by_index::const_iterator; - - table_definition() = default; - table_definition(std::initializer_list columns); - explicit table_definition(const std::vector &columns); - table_definition(const table_definition &x); - table_definition& operator=(const table_definition &x); - table_definition(table_definition&&) noexcept = default; - table_definition& operator=(table_definition&&) noexcept = default; - ~table_definition() = default; - - [[nodiscard]] bool has_primary_key() const; - [[nodiscard]] std::optional primary_key() const; - - template < typename Type > - void append(const std::string &name, long size = -1) - { - append(make_column(name, size)); - } - void append(column_definition col); - - [[nodiscard]] const std::vector& columns() const; - - [[nodiscard]] const column_definition& at(const column &col) const; - [[nodiscard]] const column_definition& at(size_t index) const; - - iterator find(const std::string &column_name); - [[nodiscard]] const_iterator find(const std::string &column_name) const; - - iterator begin(); - [[nodiscard]] const_iterator begin() const; - [[nodiscard]] const_iterator cbegin() const; - - iterator end(); - [[nodiscard]] const_iterator end() const; - [[nodiscard]] const_iterator cend() const; - - [[nodiscard]] size_t size() const; - [[nodiscard]] bool empty() const; - void clear(); - -private: - void init(); - void add_to_map(column_definition &col, size_t index); - -private: - column_by_index columns_; - column_by_name_map columns_by_name_; - - int pk_index_{-1}; -}; - -} -#endif //QUERY_TABLE_DEFINITION_HPP diff --git a/include/matador/sql/to_value.hpp b/include/matador/sql/to_value.hpp deleted file mode 100644 index d4084b0..0000000 --- a/include/matador/sql/to_value.hpp +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef QUERY_TO_VALUE_HPP -#define QUERY_TO_VALUE_HPP - -#include -#include -#include -#include -#include -#include -#include - -namespace matador::sql { - -template < class Type > -void to_value(Type &value, const char *str, typename std::enable_if::value && std::is_signed::value>::type* = nullptr) -{ - if (strlen(str) == 0) { - return; - } - char *end; - errno = 0; - auto result = strtoll(str, &end, 10); - - // Check for various possible errors - if ((errno == ERANGE && (result == LLONG_MAX || result == LLONG_MIN)) || (errno != 0 && result == 0)) { - throw std::logic_error(strerror(errno)); - // Handle error - } else if (end == str) { - // No digits found - throw std::logic_error("failed to convert value to signed number: no digits were found"); - } - - value = static_cast(result); -} - -template < class Type > -void to_value(Type &value, const char *str, typename std::enable_if::value && std::is_unsigned::value>::type* = nullptr) -{ - if (strlen(str) == 0) { - return; - } - - char *end; - errno = 0; - auto result = strtoull(str, &end, 10); - - // Check for various possible errors - if ((errno == ERANGE && (result == LLONG_MAX || result == LLONG_MIN)) || (errno != 0 && result == 0)) { - throw std::logic_error(strerror(errno)); - // Handle error - } else if (end == str) { - // No digits found - throw std::logic_error("failed to convert value to unsigned number: no digits were found"); - } - - value = static_cast(result); -} - -template < class Type > -void to_value(Type &value, const char *str, typename std::enable_if::value>::type* = nullptr) -{ - if (strlen(str) == 0) { - return; - } - - char *end; - errno = 0; - auto result = strtold(str, &end); - - // Check for various possible errors - if ((errno == ERANGE && (result == LDBL_MAX || result == LDBL_MIN)) || (errno != 0 && result == 0)) { - throw std::logic_error(strerror(errno)); - // Handle error - } else if (end == str) { - // No digits found - throw std::logic_error("failed to convert value to floating point number: no digits were found"); - } - - value = static_cast(result); -} - -} - -#endif //QUERY_TO_VALUE_HPP diff --git a/include/matador/sql/value_extractor.hpp b/include/matador/sql/value_extractor.hpp deleted file mode 100644 index 89f786f..0000000 --- a/include/matador/sql/value_extractor.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef QUERY_VALUE_EXTRACTOR_HPP -#define QUERY_VALUE_EXTRACTOR_HPP - -#include "matador/sql/fk_value_extractor.hpp" -#include "matador/sql/data_type_traits.hpp" - -#include "matador/utils/foreign_attributes.hpp" - -#include - -namespace matador::sql { - -class value_extractor -{ -private: - explicit value_extractor(std::vector &values); - -public: - ~value_extractor() = default; - - template < class Type > - static std::vector extract(const Type &type) - { - std::vector values; - value_extractor gen(values); - matador::utils::access::process(gen, type); - return std::move(values); - } - - template - void on_primary_key(const char *, ValueType &x, typename std::enable_if::value && !std::is_same::value>::type* = 0) - { - append(x); - } - void on_primary_key(const char *id, std::string &pk, size_t size); - void on_revision(const char *id, unsigned long long &rev); - template < class Type > - void on_attribute(const char *, Type &x, const utils::field_attributes &/*attr*/ = utils::null_attributes) - { - append(x); - } - void on_attribute(const char *id, char *x, const utils::field_attributes &/*attr*/ = utils::null_attributes); - void on_attribute(const char *id, std::string &x, const utils::field_attributes &/*attr*/ = utils::null_attributes); - - template class Pointer> - void on_belongs_to(const char * /*id*/, Pointer &x, const utils::foreign_attributes &/*attr*/) - { - values_.emplace_back(fk_value_extractor_.extract(*x)); - } - template class Pointer> - void on_has_one(const char * /*id*/, Pointer &x, const utils::foreign_attributes &/*attr*/) - { - values_.emplace_back(fk_value_extractor_.extract(*x)); - } - template - void on_has_many(ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {} - template - void on_has_many_to_many(const char *id, ContainerType &c, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &/*attr*/) {} - template - void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {} - -private: - template - void append(Type &value) - { - values_.emplace_back(data_type_traits::create_value(value)); - } - -private: - detail::fk_value_extractor fk_value_extractor_; - std::vector &values_; -}; - -} - -#endif //QUERY_VALUE_EXTRACTOR_HPP diff --git a/include/matador/utils/access.hpp b/include/matador/utils/access.hpp index 48338cc..7356dfc 100644 --- a/include/matador/utils/access.hpp +++ b/include/matador/utils/access.hpp @@ -1,19 +1,22 @@ -#ifndef QUERY_ACCESS_HPP -#define QUERY_ACCESS_HPP +#ifndef OOS_ACCESS_HPP +#define OOS_ACCESS_HPP #include #include -namespace matador::utils { +namespace matador { + +enum class cascade_type; template < class Type, template < class ... > class ContainerType > class container; +namespace utils { class field_attributes; class foreign_attributes; +} namespace access { - template void process(Operator &op, Type &object) { object.process(op); @@ -40,12 +43,12 @@ void revision(Operator &op, const char *id, unsigned long long &value) { } template -void attribute(Operator &op, const char *id, Type &value, const field_attributes &attr) { +void attribute(Operator &op, const char *id, Type &value, const utils::field_attributes &attr) { op.on_attribute(id, value, attr); } template -void attribute(Operator &op, const char *id, std::optional &value, const field_attributes &attr) { +void attribute(Operator &op, const char *id, std::optional &value, const utils::field_attributes &attr) { op.on_attribute(id, value, attr); } @@ -55,32 +58,67 @@ void attribute(Operator &op, const char *id, Type &value) { } template -void has_one(Operator &op, const char *id, Type &value, const foreign_attributes &attr) { +void has_one(Operator &op, const char *id, Type &value, const utils::foreign_attributes &attr) { op.on_has_one(id, value, attr); } template -void belongs_to(Operator &op, const char *id, Type &value, const foreign_attributes &attr) { +void has_one(Operator &op, const char *id, Type &value) { + op.on_has_one(id, value); +} + +template +void belongs_to(Operator &op, const char *id, Type &value, const utils::foreign_attributes &attr) { op.on_belongs_to(id, value, attr); } -template class ContainerType> -void has_many(Operator &op, ContainerType &container, const char *join_column, const foreign_attributes &attr) { - op.on_has_many(container, join_column, attr); +template +void belongs_to(Operator &op, const char *id, Type &value) { + op.on_belongs_to(id, value); } template class ContainerType> -void has_many_to_many(Operator &op, const char *id, ContainerType &container, const char *join_column, const char *inverse_join_column, const foreign_attributes &attr) { - op.on_has_many_to_many(id, container, join_column, inverse_join_column, attr); +void has_many(Operator &op, const char *id, container &c, const char *join_column, const utils::foreign_attributes &attr) { + op.on_has_many(id, c, join_column, attr); } template class ContainerType> -void has_many_to_many(Operator &op, const char *id, ContainerType &container, const foreign_attributes &attr) { - op.on_has_many_to_many(id, container, attr); +void has_many(Operator &op, const char *id, container &c, const char *join_column) { + op.on_has_many(id, c, join_column); +} + +template class ContainerType> +void has_many(Operator &op, const char *id, container &c, const utils::foreign_attributes &attr) { + op.on_has_many(id, c, attr); +} + +template class ContainerType> +void has_many(Operator &op, const char *id, container &c) { + op.on_has_many(id, c); +} + +template +void has_many_to_many(Operator &op, const char *id, ContainerType &c, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &attr) { + op.on_has_many_to_many(id, c, join_column, inverse_join_column, attr); +} + +template +void has_many_to_many(Operator &op, const char *id, ContainerType &c, const char *join_column, const char *inverse_join_column) { + op.on_has_many_to_many(id, c, join_column, inverse_join_column); +} + +template +void has_many_to_many(Operator &op, const char *id, ContainerType &c, const utils::foreign_attributes &attr) { + op.on_has_many_to_many(id, c, attr); +} + +template +void has_many_to_many(Operator &op, const char *id, ContainerType &c) { + op.on_has_many_to_many(id, c); } } } -#endif //QUERY_ACCESS_HPP +#endif //OOS_ACCESS_HPP diff --git a/include/matador/utils/attribute_reader.hpp b/include/matador/utils/attribute_reader.hpp new file mode 100644 index 0000000..7d69f32 --- /dev/null +++ b/include/matador/utils/attribute_reader.hpp @@ -0,0 +1,44 @@ +#ifndef MATADOR_ATTRIBUTE_READER_HPP +#define MATADOR_ATTRIBUTE_READER_HPP + +#include "matador/utils/types.hpp" + +#include +#include + +namespace matador { +class date; +class time; +} +namespace matador::utils { + +class value; + +class attribute_reader +{ +public: + virtual ~attribute_reader() = default; + + virtual void read_value(const char *id, size_t index, int8_t &value) = 0; + virtual void read_value(const char *id, size_t index, int16_t &value) = 0; + virtual void read_value(const char *id, size_t index, int32_t &value) = 0; + virtual void read_value(const char *id, size_t index, int64_t &value) = 0; + virtual void read_value(const char *id, size_t index, uint8_t &value) = 0; + virtual void read_value(const char *id, size_t index, uint16_t &value) = 0; + virtual void read_value(const char *id, size_t index, uint32_t &value) = 0; + virtual void read_value(const char *id, size_t index, uint64_t &value) = 0; + virtual void read_value(const char *id, size_t index, bool &value) = 0; + virtual void read_value(const char *id, size_t index, float &value) = 0; + virtual void read_value(const char *id, size_t index, double &value) = 0; + virtual void read_value(const char *id, size_t index, time &value) = 0; + virtual void read_value(const char *id, size_t index, date &value) = 0; + virtual void read_value(const char *id, size_t index, char *value, size_t size) = 0; + virtual void read_value(const char *id, size_t index, std::string &value) = 0; + virtual void read_value(const char *id, size_t index, std::string &value, size_t size) = 0; + virtual void read_value(const char *id, size_t index, blob &value) = 0; + virtual void read_value(const char *id, size_t index, value &value, size_t size) = 0; +}; + +} + +#endif //MATADOR_ATTRIBUTE_READER_HPP diff --git a/include/matador/utils/attribute_writer.hpp b/include/matador/utils/attribute_writer.hpp new file mode 100644 index 0000000..ceec80c --- /dev/null +++ b/include/matador/utils/attribute_writer.hpp @@ -0,0 +1,44 @@ +#ifndef MATADOR_ATTRIBUTE_BINDER_HPP +#define MATADOR_ATTRIBUTE_BINDER_HPP + +#include "matador/utils/types.hpp" + +#include + +namespace matador { +class date; +class time; +} +namespace matador::utils { + +class value; + +class attribute_writer +{ +public: + virtual ~attribute_writer() = default; + + virtual void write_value(size_t pos, const int8_t &x) = 0; + virtual void write_value(size_t pos, const int16_t &x) = 0; + virtual void write_value(size_t pos, const int32_t &x) = 0; + virtual void write_value(size_t pos, const int64_t &x) = 0; + virtual void write_value(size_t pos, const uint8_t &x) = 0; + virtual void write_value(size_t pos, const uint16_t &x) = 0; + virtual void write_value(size_t pos, const uint32_t &x) = 0; + virtual void write_value(size_t pos, const uint64_t &x) = 0; + virtual void write_value(size_t pos, const bool &x) = 0; + virtual void write_value(size_t pos, const float &x) = 0; + virtual void write_value(size_t pos, const double &x) = 0; + virtual void write_value(size_t pos, const time &x) = 0; + virtual void write_value(size_t pos, const date &x) = 0; + virtual void write_value(size_t pos, const char *x) = 0; + virtual void write_value(size_t pos, const char *x, size_t size) = 0; + virtual void write_value(size_t pos, const std::string &x) = 0; + virtual void write_value(size_t pos, const std::string &x, size_t size) = 0; + virtual void write_value(size_t pos, const blob &x) = 0; + virtual void write_value(size_t pos, const value &x, size_t size) = 0; +}; + +} + +#endif //MATADOR_ATTRIBUTE_BINDER_HPP diff --git a/include/matador/utils/base_class.hpp b/include/matador/utils/base_class.hpp new file mode 100644 index 0000000..63b4c49 --- /dev/null +++ b/include/matador/utils/base_class.hpp @@ -0,0 +1,42 @@ +#ifndef BASE_CLASS_HPP +#define BASE_CLASS_HPP + +#include + +namespace matador { + +/** + * @brief Safely casts a given derived class to its base class + * + * @tparam B The base class type + * @tparam D The class type of the derived class + * @param derived The derived object + * @return The casted object + */ +template < class B, class D> +const B* base_class(const D *derived) +{ + static_assert(!std::is_same_v, "class B must not be of same type as class D"); + static_assert(std::is_base_of_v, "class B must be base of class D"); + return static_cast(derived); +} + +/** + * @brief Safely casts a given derived class to its base class + * + * @tparam B The base class type + * @tparam D The class type of the derived class + * @param derived The derived object + * @return The casted object + */ +template < class B, class D> +B* base_class(D *derived) +{ + static_assert(!std::is_same_v, "class B must not be of same type as class D"); + static_assert(std::is_base_of_v, "class B must be base of class D"); + return static_cast(derived); +} + +} + +#endif /* BASE_CLASS_HPP */ diff --git a/include/matador/utils/basic_type_converter.hpp b/include/matador/utils/basic_type_converter.hpp new file mode 100644 index 0000000..cf89744 --- /dev/null +++ b/include/matador/utils/basic_type_converter.hpp @@ -0,0 +1,55 @@ +#ifndef QUERY_ANY_TYPE_TO_VISITOR_HPP +#define QUERY_ANY_TYPE_TO_VISITOR_HPP + +#include "matador/utils/convert.hpp" + +#include +#include +#include + +// namespace matador { +//class date; +//class time; +//} + +namespace matador::utils { + +template < typename Type > +class basic_type_converter { +public: + static result convert_value(const database_type& from) { + basic_type_converter converter; + std::visit(converter, const_cast(from)); + + return converter.result_; + } + + void operator()(int8_t &x) { this->convert(x); } + void operator()(int16_t &x) { this->convert(x); } + void operator()(int32_t &x) { this->convert(x); } + void operator()(int64_t &x) { this->convert(x); } + void operator()(uint8_t &x) { this->convert(x); } + void operator()(uint16_t &x) { this->convert(x); } + void operator()(uint32_t &x) { this->convert(x); } + void operator()(uint64_t &x) { this->convert(x); } + void operator()(bool &x) { this->convert(x); } + void operator()(float &x) { this->convert(x); } + void operator()(double &x) { this->convert(x); } + void operator()(const char *x) { this->convert(x); } + void operator()(std::string &x) { this->convert(x); } +// void operator()(date &x) { this->convert(result, x); } +// void operator()(time &x) { this->convert(result, x); } + void operator()(blob &x) { this->convert(x); } + +private: + result result_{}; + + template< typename FromType > + void convert(FromType &from) { + result_ = utils::to(from); + } +}; + +} + +#endif //QUERY_ANY_TYPE_TO_VISITOR_HPP diff --git a/include/matador/utils/basic_types.hpp b/include/matador/utils/basic_types.hpp new file mode 100644 index 0000000..ea873c8 --- /dev/null +++ b/include/matador/utils/basic_types.hpp @@ -0,0 +1,33 @@ +#ifndef BASIC_TYPES_HPP +#define BASIC_TYPES_HPP + +#include + +namespace matador::utils { + +/** + * @brief Enumeration type of all supported basic data types + */ +enum class basic_type : uint8_t { + type_int8 = 0, /*!< Data type int8 */ + type_int16, /*!< Data type int16 */ + type_int32, /*!< Data type int32 */ + type_int64, /*!< Data type int64 */ + type_uint8, /*!< Data type unsigned int8 */ + type_uint16, /*!< Data type unsigned int16 */ + type_uint32, /*!< Data type unsigned int32 */ + type_uint64, /*!< Data type unsigned int64 */ + type_float, /*!< Data type float */ + type_double, /*!< Data type double */ + type_bool, /*!< Data type bool */ + type_varchar, /*!< Data type varchar */ + type_text, /*!< Data type text */ + type_date, /*!< Data type date */ + type_time, /*!< Data type time */ + type_blob, /*!< Data type blob */ + type_null /*!< Data type null */ +}; + +} + +#endif //BASIC_TYPES_HPP diff --git a/include/matador/utils/cascade_type.hpp b/include/matador/utils/cascade_type.hpp index 2132cb2..a102499 100644 --- a/include/matador/utils/cascade_type.hpp +++ b/include/matador/utils/cascade_type.hpp @@ -1,14 +1,12 @@ -#ifndef QUERY_CASCADE_TYPE_HPP -#define QUERY_CASCADE_TYPE_HPP - -#include +#ifndef OOS_CASCADE_TYPE_HPP +#define OOS_CASCADE_TYPE_HPP namespace matador::utils { /** * @brief Cascade types for database actions */ -enum class cascade_type : uint8_t +enum class cascade_type { NONE = 0, /**< Cascade type none */ REMOVE = 1, /**< Cascade type remove */ @@ -26,5 +24,4 @@ inline cascade_type& operator&= (cascade_type& a, cascade_type b) { return (casc inline cascade_type& operator^= (cascade_type& a, cascade_type b) { return (cascade_type&)((int&)a ^= (int)b); } } - -#endif //QUERY_CASCADE_TYPE_HPP +#endif //OOS_CASCADE_TYPE_HPP diff --git a/include/matador/utils/constraints.hpp b/include/matador/utils/constraints.hpp index 4b4dca6..44b0b18 100644 --- a/include/matador/utils/constraints.hpp +++ b/include/matador/utils/constraints.hpp @@ -1,20 +1,19 @@ -#ifndef QUERY_CONSTRAINTS_HPP -#define QUERY_CONSTRAINTS_HPP +#ifndef MATADOR_CONSTRAINTS_HPP +#define MATADOR_CONSTRAINTS_HPP namespace matador::utils { enum class constraints : unsigned char { - NONE = 0, - INDEX = 1 << 1, - UNIQUE = 1 << 2, - PRIMARY_KEY = 1 << 3, - FOREIGN_KEY = 1 << 4, - DEFAULT = 1 << 5, - AUTO_INCREMENT = 1 << 6 + NONE = 0, +// NOT_NULL = 1 << 0, + INDEX = 1 << 1, + UNIQUE = 1 << 2, + PRIMARY_KEY = 1 << 3, + FOREIGN_KEY = 1 << 4, + DEFAULT = 1 << 5 +// UNIQUE_NOT_NULL = UNIQUE | NOT_NULL }; -//static std::unordered_map constraints_to_name_map(); - inline constraints operator|(constraints a, constraints b) { return static_cast(static_cast(a) | static_cast(b)); @@ -34,4 +33,5 @@ inline bool is_constraint_set(constraints source, constraints needle) } } -#endif //QUERY_CONSTRAINTS_HPP + +#endif //MATADOR_CONSTRAINTS_HPP diff --git a/include/matador/utils/convert.hpp b/include/matador/utils/convert.hpp new file mode 100644 index 0000000..dd62ddf --- /dev/null +++ b/include/matador/utils/convert.hpp @@ -0,0 +1,239 @@ +#ifndef MATADOR_CONVERT_HPP +#define MATADOR_CONVERT_HPP + +#include "matador/utils/types.hpp" +#include "matador/utils/result.hpp" +//#include "matador/utils/date.hpp" +//#include "matador/utils/time.hpp" + +//#include "matador/utils/placeholder.hpp" + +#include +#include +#include +#include +#include +#include +#include + +/* + * Conversion matrix + * from> | int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | bool | float | double | string | date | time | blob | placeholder | null + * to | | | | | | | | | | | | | | | | | + * ------------+------+-------+-------+-------+-------+--------+--------+--------+------+-------+--------+--------+------+------+------+-------------+----- + * int8 | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | try | ok | ok | try | N/A | N/A + * int16 | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | try | ok | ok | try | N/A | N/A + * int32 | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | try | ok | ok | try | N/A | N/A + * int64 | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | try | ok | ok | try | N/A | N/A + * uint8 | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | try | ok | ok | try | N/A | N/A + * uint16 | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | try | ok | ok | try | N/A | N/A + * uint32 | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | try | ok | ok | try | N/A | N/A + * uint64 | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | try | ok | ok | try | N/A | N/A + * bool | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | try | N/A | N/A | try | N/A | N/A + * float | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | try | N/A | N/A | try | N/A | N/A + * double | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | try | N/A | N/A | try | N/A | N/A + * string | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | N/A | N/A | N/A + * date | ok | ok | ok | ok | ok | ok | ok | ok | N/A | N/A | N/A | try | ok | ok | N/A | N/A | N/A + * time | ok | ok | ok | ok | ok | ok | ok | ok | N/A | N/A | N/A | try | ok | ok | N/A | N/A | N/A + * blob | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | ok | N/A | N/A + * placeholder | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | ok | N/A + * null | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | ok + * + * from integral to date/time works, value is interpreted as std::chrono::timepoint + * from integral to blob works, bytes of integral data will converted to blob + * from boolean to blob works, byte of bool data will converted to blob + * from floating point to blob works, bytes of floating point data will converted to blob + * from date to blob works, bytes of date will converted to blob + * from time to blob works, bytes of time will converted to blob + * from string to blob works, bytes of string data will converted to blob + * from floating point to integral works, fractions are truncated + * from date/time to integral works, value will be converted from std::chrono::timepoint + * from blob to integral works, value will be converted missing is filled with zero, rest is omitted + * from blob to floating point works, value will be converted missing is filled with zero, rest is omitted + * from blob to boolean works, value will be converted missing is filled with zero, rest is omitted + */ + +namespace matador::utils { + +enum class conversion_error { + Ok, + NotConvertable, + MissingData +}; + +enum class conversion_policy { + Strict, + Relax +}; + + +// template < typename DestType, typename SourceType > +// result to(const SourceType &/*from*/); +// result to(const SourceType &/*from*/, conversion_policy /*policy*/ = conversion_policy::Strict); + +// { + // return failure(conversion_error::NotConvertable); +// } + +/* + * Integral, Floating point & bool conversion + */ +template < typename DestType, typename SourceType > +result to(const SourceType &source, std::enable_if_t && std::is_arithmetic_v>* = nullptr) +{ + return ok(static_cast(source)); +} + +template < typename DestType, typename SourceType > +result to(const SourceType &source, std::enable_if_t && std::is_same_v>* = nullptr) +{ + return ok(source); +} + +template < typename DestType, typename SourceType > +result to(const SourceType &source, std::enable_if_t && !std::is_same_v && std::is_same_v>* = nullptr) +{ + std::array buffer{}; + auto [ptr, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), source, 10); + if (ec == std::errc{}) { + return ok(std::string(buffer.data(), ptr)); + } + + // return failure({ec, "couldn't convert value to std::string"}); + return failure(conversion_error::NotConvertable/*, "couldn't convert value to std::string"}*/); +} + +template < typename DestType, typename SourceType > +result to(const SourceType &source, std::enable_if_t && std::is_same_v>* = nullptr) +{ + std::array buffer{}; + auto [ptr, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), source, std::chars_format::general); + if (ec == std::errc{}) { + return ok(std::string(buffer.data(), ptr)); + } + + // return failure({ec, "couldn't convert value to std::string"}); + return failure(conversion_error::NotConvertable/*, "couldn't convert value to std::string"}*/); +} + +template < typename DestType > +result to(const std::string &source, std::enable_if_t && std::is_signed_v>* = nullptr) +{ + if (source.empty()) { + return failure(conversion_error::MissingData/*, "failed to convert empty string"}*/); + } + char *end; + errno = 0; + const auto result = strtoll(source.c_str(), &end, 10); + if (errno == ERANGE) { + return failure(conversion_error::NotConvertable/*, "failed to convert string value"}*/); + } + + return ok(static_cast(result)); +} + +template < typename DestType > +result to(const std::string &source, std::enable_if_t && std::is_unsigned_v>* = nullptr) +{ + if (source.empty()) { + return failure(conversion_error::MissingData/*, "failed to convert empty string"}*/); + } + char *end; + errno = 0; + const auto result = strtoull(source.c_str(), &end, 10); + if (errno == ERANGE) { + return failure(conversion_error::NotConvertable/*, "failed to convert string value"}*/); + } + + return ok(static_cast(result)); +} + +template < typename DestType > +result to(const std::string &source, std::enable_if_t>* = nullptr) +{ + if (source.empty()) { + return failure(conversion_error::MissingData/*, "failed to convert empty string"}*/); + } + char *end; + errno = 0; + const auto result = strtod(source.c_str(), &end); + if (errno == ERANGE) { + return failure(conversion_error::NotConvertable/*, "failed to convert string value"}*/); + } + + return ok(static_cast(result)); +} + +template < typename DestType > +result to(const blob &source, std::enable_if_t>* = nullptr) { + return ok(source); +} + +static std::unordered_map string_to_bool_map = { + {"true", true}, + {"on", true}, + {"1", true}, + {"false", false}, + {"off", false}, + {"0", false} +}; + +template < typename DestType, typename SourceType > +result to(const SourceType &source, std::enable_if_t &&std::is_same_v>* = nullptr) { + if (source.empty()) { + return failure(conversion_error::MissingData); + } + const auto it = string_to_bool_map.find(source); + if (it == string_to_bool_map.end()) { + return failure(conversion_error::NotConvertable); + } + return ok(it->second); +} + +template < typename DestType, typename SourceType > +result to(const SourceType &source, std::enable_if_t &&std::is_same_v>* = nullptr) { + return ok(std::string(source ? "true" : "false")); +} + +template < typename DestType, typename SourceType > +result to(const SourceType &/*source*/, std::enable_if_t &&std::is_same_v>* = nullptr) { + return failure(conversion_error::NotConvertable); +} + +template < typename DestType, typename SourceType > +result to(const SourceType &source, std::enable_if_t &&std::is_same_v>* = nullptr) { + return ok(std::string(source)); +} + +template < typename DestType, typename SourceType > +result to(const SourceType &/*source*/, std::enable_if_t && std::is_same_v>* = nullptr) { + return failure(conversion_error::NotConvertable); +} + +template < typename DestType, typename SourceType > +result to(const SourceType &source, std::enable_if_t && std::is_same_v>* = nullptr) { + DestType result; + result.resize(sizeof(source)); + std::memcpy(result.data(), &source, sizeof(source)); + + return ok(result); +} + +template < typename DestType, typename SourceType > +result to(const SourceType &/*source*/, std::enable_if_t && std::is_same_v>* = nullptr) { + return failure(conversion_error::NotConvertable); +} + +template < typename DestType, typename SourceType > +result to(const SourceType &/*source*/, std::enable_if_t && std::is_same_v>* = nullptr) { + return failure(conversion_error::NotConvertable); +} + +template < typename DestType, typename SourceType > +result to(const SourceType &/*source*/, std::enable_if_t && std::is_same_v>* = nullptr) { + return failure(conversion_error::NotConvertable); +} + +} + +#endif //MATADOR_CONVERT_HPP diff --git a/include/matador/utils/data_type_traits.hpp b/include/matador/utils/data_type_traits.hpp new file mode 100644 index 0000000..ffb00fb --- /dev/null +++ b/include/matador/utils/data_type_traits.hpp @@ -0,0 +1,25 @@ +#ifndef BASIC_TYPE_TRAITS_HPP +#define BASIC_TYPE_TRAITS_HPP + +#include "matador/utils/basic_types.hpp" + +#include + +namespace matador::utils { + +class attribute_reader; +class attribute_writer; + +/** + * @tparam Type The type of the traits + * @brief Type traits for database types + * + * This class is used to determine and + * provide the correct size information + * for a data type + */ +template < class Type, class Enable = void > +struct data_type_traits; + +} +#endif //BASIC_TYPE_TRAITS_HPP diff --git a/include/matador/utils/default_type_traits.hpp b/include/matador/utils/default_type_traits.hpp new file mode 100644 index 0000000..1ee2b52 --- /dev/null +++ b/include/matador/utils/default_type_traits.hpp @@ -0,0 +1,168 @@ +#ifndef MATADOR_DEFAULT_TYPE_TRAITS_HPP +#define MATADOR_DEFAULT_TYPE_TRAITS_HPP + +#include "matador/utils/data_type_traits.hpp" +#include "matador/utils/types.hpp" + +#include +#include + +namespace matador::utils { + +/// @cond MATADOR_DEV +template <> struct data_type_traits +{ + static basic_type type(std::size_t /*size*/) { return basic_type::type_null; } + static void read_value(attribute_reader &reader, const char *id, size_t index, nullptr_t &/*value*/); + static void bind_value(attribute_writer &binder, size_t index, nullptr_t &/*value*/); +}; + +template <> struct data_type_traits +{ + static basic_type type(std::size_t /*size*/ = 0) { return basic_type::type_int8; } + static void read_value(attribute_reader &reader, const char *id, size_t index, int8_t &value); + static void bind_value(attribute_writer &binder, size_t index, const int8_t &value); +}; + +template <> struct data_type_traits +{ + static basic_type type(std::size_t /*size*/ = 0) { return basic_type::type_int16; } + static void read_value(attribute_reader &reader, const char *id, size_t index, int16_t &value); + static void bind_value(attribute_writer &binder, size_t index, const int16_t &value); +}; + +template <> struct data_type_traits +{ + static basic_type type(std::size_t /*size*/ = 0) { return basic_type::type_int32; } + static void read_value(attribute_reader &reader, const char *id, size_t index, int32_t &value); + static void bind_value(attribute_writer &binder, size_t index, const int32_t &value); +}; + +template <> struct data_type_traits +{ + static basic_type type(std::size_t /*size*/ = 0) { return basic_type::type_int64; } + static void read_value(attribute_reader &reader, const char *id, size_t index, int64_t &value); + static void bind_value(attribute_writer &binder, size_t index, const int64_t &value); +}; + +template <> struct data_type_traits +{ + static basic_type type(std::size_t /*size*/ = 0) { return basic_type::type_uint8; } + static void read_value(attribute_reader &reader, const char *id, size_t index, uint8_t &value); + static void bind_value(attribute_writer &binder, size_t index, const uint8_t &value); +}; + +template <> struct data_type_traits +{ + static basic_type type(std::size_t /*size*/ = 0) { return basic_type::type_uint16; } + static void read_value(attribute_reader &reader, const char *id, size_t index, uint16_t &value); + static void bind_value(attribute_writer &binder, size_t index, const uint16_t &value); +}; + +template <> struct data_type_traits +{ + static basic_type type(std::size_t /*size*/ = 0) { return basic_type::type_uint32; } + static void read_value(attribute_reader &reader, const char *id, size_t index, uint32_t &value); + static void bind_value(attribute_writer &binder, size_t index, const uint32_t &value); +}; + +template <> struct data_type_traits +{ + static basic_type type(std::size_t /*size*/ = 0) { return basic_type::type_uint64; } + static void read_value(attribute_reader &reader, const char *id, size_t index, uint64_t &value); + static void bind_value(attribute_writer &binder, size_t index, const uint64_t &value); +}; + +template <> struct data_type_traits +{ + static basic_type type(std::size_t /*size*/ = 0) { return basic_type::type_bool; } + static void read_value(attribute_reader &reader, const char *id, size_t index, bool &value); + static void bind_value(attribute_writer &binder, size_t index, const bool &value); +}; + +template <> struct data_type_traits +{ + static basic_type type(std::size_t /*size*/ = 0) { return basic_type::type_float; } + static void read_value(attribute_reader &reader, const char *id, size_t index, float &value); + static void bind_value(attribute_writer &binder, size_t index, const float &value); +}; + +template <> struct data_type_traits +{ + static basic_type type(std::size_t /*size*/ = 0) { return basic_type::type_double; } + static void read_value(attribute_reader &reader, const char *id, size_t index, double &value); + static void bind_value(attribute_writer &binder, size_t index, const double &value); +}; + +template <> struct data_type_traits +{ + static basic_type type(const std::size_t size) { return size == 0 ? basic_type::type_text : basic_type::type_varchar; } + static void read_value(attribute_reader &reader, const char *id, size_t index, const char* value, size_t size); + static void bind_value(attribute_writer &binder, size_t index, const char *value, size_t size = 0); +}; + +template <> struct data_type_traits +{ + static basic_type type(const std::size_t size) { return size == 0 ? basic_type::type_text : basic_type::type_varchar; } + static void read_value(attribute_reader &reader, const char *id, size_t index, char *value, size_t size); + static void bind_value(attribute_writer &binder, size_t index, const char *value, size_t size = 0); +}; + +template <> struct data_type_traits +{ + static basic_type type(const std::size_t size) { return size == 0 ? basic_type::type_text : basic_type::type_varchar; } + template < int N > + static void read_value(attribute_reader &reader, const char *id, const size_t index, char (&value)[N], const size_t size) { + data_type_traits::read_value(reader, id, index, value, size); + } + template < int N > + static void bind_value(attribute_writer &binder, const size_t index, char *value, const size_t size = 0) { + data_type_traits::bind_value(binder, index, value, size); + } +}; + +template <> struct data_type_traits +{ + static basic_type type(const std::size_t size) { return size == 0 ? basic_type::type_text : basic_type::type_varchar; } + static void read_value(attribute_reader &reader, const char *id, size_t index, std::string &value, size_t size); + static void bind_value(attribute_writer &binder, size_t index, std::string &value, size_t size = 0); +}; + +template <> struct data_type_traits +{ + static basic_type type(std::size_t /*size*/) { return basic_type::type_blob; } + static void read_value(attribute_reader &reader, const char *id, size_t index, utils::blob &value); + static void bind_value(attribute_writer &binder, size_t index, utils::blob &value); +}; + +//template <> struct data_type_traits +//{ +// static basic_type type(std::size_t /*size*/) { return basic_type::type_date; } +// static void read_value(attribute_reader &reader, const char *id, size_t index, matador::date &value); +// static void bind_value(attribute_writer &binder, size_t index, matador::date &value); +//}; +// +//template <> struct data_type_traits +//{ +// static basic_type type(std::size_t /*size*/) { return basic_type::type_time; } +// static void read_value(attribute_reader &reader, const char *id, size_t index, matador::time &value); +// static void bind_value(attribute_writer &binder, size_t index, matador::time &value); +//}; + +template < typename EnumType > +struct data_type_traits>> +{ + static basic_type type(std::size_t /*size*/ = 0) { return basic_type::type_int32; } + static void read_value(attribute_reader &reader, const char *id, const size_t index, EnumType &value) + { + data_type_traits::read_value(reader, id, index, reinterpret_cast(value)); + } + static void bind_value(attribute_writer &binder, const size_t index, EnumType &value) + { + data_type_traits::bind_value(binder, index, static_cast(value)); + } +}; +/// @endcond + +} +#endif //MATADOR_DEFAULT_TYPE_TRAITS_HPP diff --git a/include/matador/utils/di.hpp b/include/matador/utils/di.hpp new file mode 100644 index 0000000..2c75854 --- /dev/null +++ b/include/matador/utils/di.hpp @@ -0,0 +1,385 @@ +#ifndef MATADOR_DI_HPP +#define MATADOR_DI_HPP + +#include "matador/utils/singleton.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace matador::utils::di { +/** + * Interface for the dependency injection + * creation strategy. + * + * The injected service + */ +class strategy +{ +public: + /** + * Destructor + */ + virtual ~strategy() = default; + + /** + * @brief Acquires an instance + * + * Acquires an instance based on the internal strategy + * and returns an anonymous pointer to the object + * + * @return The current injected object depending on the strategy + */ + virtual void *acquire() = 0; +}; + +/** + * Implements the transient dependency injection + * strategy. When ever injected a new instance + * is created. + * + * @tparam T Type of the object to be injected + */ +template +class transient_strategy final : public strategy +{ +public: + /** + * @brief Construct a new transient strategy object + * + * @tparam Args Type of arguments for constructor + * @param args Arguments for constructor + */ + template + explicit transient_strategy(Args &&...args) { + creator_ = [args..., this]() { + instances_.push_back(std::make_unique(args...)); + return instances_.back().get(); + }; + } + + void *acquire() override { + return creator_(); + } + +private: + std::function creator_{}; + std::vector> instances_; +}; + +/** + * @brief Implements the singleton strategy + * + * The singleton strategy provides only one + * instance of the type. + * + * @tparam T Type of the object to be injected + */ +template +class singleton_strategy : public strategy +{ +public: + template + explicit singleton_strategy(Args &&...args) { + creator_ = [args..., this]() { + if (!instance_) { + instance_ = std::make_unique(args...); + } + return instance_.get(); + }; + } + + void *acquire() override { + return creator_(); + } + +private: + std::function creator_{}; + std::unique_ptr instance_; +}; + +template +class singleton_per_thread_strategy : public strategy +{ +public: + template + explicit singleton_per_thread_strategy(Args &&...args) { + creator_ = [args..., this]() { + thread_local auto instance = std::make_unique(args...); + std::lock_guard lock(instance_mutex_); + auto it = instance_map_.insert({std::this_thread::get_id(), std::move(instance)}).first; + return it->second.get(); + }; + } + + void *acquire() override { + return creator_(); + } + +private: + std::function creator_{}; + std::mutex instance_mutex_; + std::unordered_map> instance_map_; +}; +/** + * @brief Provides a specific instance on injection + * + * @tparam T + */ +template +class instance_strategy : public strategy +{ +public: + explicit instance_strategy(T &&obj) + : instance_(obj) {} + + void* acquire() override { + return &instance_; + } + +private: + T &instance_; +}; + +class proxy_base +{ +public: + virtual ~proxy_base() = default; + + template + T* get() const { + return static_cast(strategy_->acquire()); + } + +protected: + void initialize_strategy(std::unique_ptr &&strategy) { + strategy_ = std::move(strategy); + } + +private: + std::unique_ptr strategy_; +}; + +template +class proxy : public proxy_base +{ +public: + template> * = nullptr> + void to( Args &&...args) { + initialize_strategy(std::make_unique>(std::forward(args)...)); + } + + template + void to_instance(T &&obj) { + initialize_strategy(std::make_unique>(obj)); + } + + template> * = nullptr> + void to_singleton(Args &&...args) { + initialize_strategy(std::make_unique>(std::forward(args)...)); + } + + template> * = nullptr> + void to_singleton_per_thread(Args &&...args) { + initialize_strategy(std::make_unique>(std::forward(args)...)); + } +}; + +class module +{ +private: + using t_type_proxy_map = std::unordered_map>; + +public: + void clear() + { + default_map_.clear(); + module_map_.clear(); + } + + template + std::shared_ptr> bind() { + return bind(default_map_); + } + + template + std::shared_ptr> bind(const std::string &name) { + auto i = module_map_.find(name); + if (i == module_map_.end()) { + i = module_map_.insert(std::make_pair(name, t_type_proxy_map{})).first; + } + return bind(i->second); + } + + template + I* resolve() { + return resolve(default_map_); + } + + template + I* resolve(const std::string &name) { + auto i = module_map_.find(name); + if (i == module_map_.end()) { + throw std::logic_error("unknown name " + name); + } + return resolve(i->second); + } + +private: + template + std::shared_ptr> bind(t_type_proxy_map &type_proxy_map) { + auto di_proxy_ptr = std::make_shared>(); + auto i = type_proxy_map.insert(std::make_pair(std::type_index(typeid(I)), di_proxy_ptr)); + return std::static_pointer_cast>(i.first->second); + } + + template + static I* resolve(t_type_proxy_map &type_proxy_map) { + const auto i = type_proxy_map.find(std::type_index(typeid(I))); + if (i == type_proxy_map.end()) { + throw std::logic_error("unknown type"); + } + return i->second->get(); + } + +private: + t_type_proxy_map default_map_; + + std::unordered_map module_map_ {}; +}; + +class repository : public utils::singleton +{ +public: + void clear() + { + module_.clear(); + } + + void install(const std::function& builder) + { + module_.clear(); + builder(module_); + } + + void append(const std::function& builder) + { + builder(module_); + } + + template + I* resolve() { + return module_.resolve(); + } + + template + I* resolve(const std::string &name) { + return module_.resolve(name); + } + +private: + module module_; +}; + +/** + * @brief Resolves and provides the requested service + * + * The class inject acts a holder for the + * requested service. On construction, it + * tries to resolve the given template type + * within the global service repository. + * + * @tparam T Type of the interface + */ +template +class inject +{ +public: + inject() + : obj(repository::instance().resolve()) + {} + + explicit inject(const std::string &name) + : obj(repository::instance().resolve(name)) + {} + + explicit inject(module &m) + : obj(m.resolve()) + {} + + inject(module &m, const std::string &name) + : obj(m.resolve(name)) + {} + + inject(const inject &x) : obj(x.obj) {} + + inject& operator=(const inject &x) { + if (this != &x) { + obj = x.obj; + } + return *this; + } + + inject(inject &&x) noexcept : obj(x.obj) { + x.obj = nullptr; + } + + inject& operator=(inject &&x) noexcept { + obj = x.obj; + x.obj = nullptr; + return *this; + } + + bool operator==(const inject &x) const { + return obj == x.obj; + } + + bool operator!=(const inject &x) const { + return obj != x.obj; + } + + T* operator->() const { + return obj; + } + + T* get() const { + return obj; + } + + T& operator*() const { + return *obj; + } + + operator bool() const { + return obj != nullptr; + } + +private: + T* obj; +}; + +inline void clear_module() +{ + repository::instance().clear(); +} + +inline void install_module(const std::function& builder) +{ + repository::instance().clear(); + repository::instance().install(builder); +} + +inline void append_module(const std::function& builder) +{ + repository::instance().append(builder); +} + +} + +#endif //MATADOR_DI_HPP diff --git a/include/matador/utils/enum_mapper.hpp b/include/matador/utils/enum_mapper.hpp index b370832..bb276c5 100644 --- a/include/matador/utils/enum_mapper.hpp +++ b/include/matador/utils/enum_mapper.hpp @@ -11,7 +11,7 @@ template < typename EnumType, class Enable = void > class enum_mapper; template < typename EnumType> -class enum_mapper::value>::type> +class enum_mapper>> { public: using enum_to_string_map = std::unordered_map; @@ -33,17 +33,18 @@ public: return std::nullopt; } - std::string to_string(EnumType enum_value) const { + const std::string& to_string(EnumType enum_value) const { if (const auto it = enum_to_string_map_.find(enum_value); it != enum_to_string_map_.end()) { return it->second; } - return {}; + return empty_; } private: enum_to_string_map enum_to_string_map_; string_to_enum_map string_to_enum_map_; + std::string empty_{}; }; } diff --git a/include/matador/utils/error.hpp b/include/matador/utils/error.hpp new file mode 100644 index 0000000..6bb549b --- /dev/null +++ b/include/matador/utils/error.hpp @@ -0,0 +1,73 @@ +#ifndef ERROR_HPP +#define ERROR_HPP + +#include "matador/utils/export.hpp" + +#include +#include +#include +#include +#include +#include + +namespace matador::utils { + +class MATADOR_UTILS_API error final { +public: + error() = default; + template , int> = 0> + explicit error(ErrorEnumType err, const std::string& msg = "") + : error(err, std::vector{}, msg) + {} + template , int> = 0> + error(ErrorEnumType err, std::vector trace, std::string msg = "") + : ec_(make_error_code(err)) + , error_message_(std::move(msg)) + , error_trace_(std::move(trace)) + {} + template , int> = 0> + error(ErrorEnumType err, error error_trace, const std::string& msg = "") + : error(err, std::vector{std::move(error_trace)}, msg) + {} + template , int> = 0> + error(ErrorEnumType err, std::unordered_map infos, std::string msg = "") + : ec_(make_error_code(err)) + , error_message_(std::move(msg)) + , error_infos_(std::move(infos)) + {} + + error(const error&) = default; + error(error&&) noexcept = default; + error& operator=(const error&) = default; + error& operator=(error&&) noexcept = default; + ~error() = default; + + friend bool operator==(const error &lhs, const error &rhs); + friend bool operator==(const error &lhs, const std::error_code &rhs); + friend bool operator==(const error &lhs, const std::error_condition &rhs); + friend bool operator!=(const error &lhs, const error &rhs); + friend bool operator!=(const error &lhs, const std::error_code &rhs); + friend bool operator!=(const error &lhs, const std::error_condition &rhs); + friend bool operator<(const error &lhs, const error &rhs); + friend bool operator<(const error &lhs, const std::error_code &rhs); + + // friend std::ostream& operator<<(std::ostream &out, const error &err); + + [[nodiscard]] std::string message() const; + [[nodiscard]] std::string category() const; + [[nodiscard]] std::error_code ec() const; + [[nodiscard]] const std::vector& error_trace() const; + [[nodiscard]] std::optional error_info(const std::string &key) const; + void add_error_info(const std::string &key, std::string value); + void remove_error_info(const std::string &key); + [[nodiscard]] std::vector error_info_keys() const; + +private: + std::error_code ec_; + std::string error_message_; + std::vector error_trace_; + std::unordered_map error_infos_; +}; + +} +#endif //ERROR_HPP diff --git a/include/matador/utils/errors.hpp b/include/matador/utils/errors.hpp new file mode 100644 index 0000000..d3cad5b --- /dev/null +++ b/include/matador/utils/errors.hpp @@ -0,0 +1,29 @@ +#ifndef ERRORS_HPP +#define ERRORS_HPP + +#include "matador/utils/export.hpp" + +#include + +namespace matador::utils { + +enum class utils_error { + InvalidVersionString, +}; + +class utils_category_impl final : public std::error_category +{ +public: + [[nodiscard]] const char* name() const noexcept override; + [[nodiscard]] std::string message(int ev) const override; +}; + +const std::error_category& utils_category(); +std::error_code make_error_code(utils_error e); +std::error_condition make_error_condition(utils_error e); +} + +template <> +struct std::is_error_code_enum : true_type {}; + +#endif //ERRORS_HPP diff --git a/include/matador/utils/export.hpp b/include/matador/utils/export.hpp new file mode 100644 index 0000000..eb80c10 --- /dev/null +++ b/include/matador/utils/export.hpp @@ -0,0 +1,19 @@ +#ifndef MATADOR_UTILS_EXPORT_HPP +#define MATADOR_UTILS_EXPORT_HPP + +#ifdef _MSC_VER +#define MATADOR_UTILS_API +// #ifdef matador_utils_EXPORTS +// #define MATADOR_UTILS_API __declspec(dllexport) +// #define EXPIMP_UTILS_TEMPLATE +// #else +// #define MATADOR_UTILS_API __declspec(dllimport) +// #define EXPIMP_UTILS_TEMPLATE extern +// #endif + #define MATADOR_UTILS_API + #pragma warning(disable: 4251) +#else +#define MATADOR_UTILS_API +#endif + +#endif //MATADOR_UTILS_EXPORT_HPP \ No newline at end of file diff --git a/include/matador/utils/fetch_type.hpp b/include/matador/utils/fetch_type.hpp index 55aa5a3..4d9ee66 100644 --- a/include/matador/utils/fetch_type.hpp +++ b/include/matador/utils/fetch_type.hpp @@ -1,5 +1,5 @@ -#ifndef QUERY_FETCH_TYPE_HPP -#define QUERY_FETCH_TYPE_HPP +#ifndef MATADOR_FETCH_TYPE_HPP +#define MATADOR_FETCH_TYPE_HPP #include @@ -18,4 +18,4 @@ enum class fetch_type : uint8_t } -#endif //QUERY_FETCH_TYPE_HPP +#endif //MATADOR_FETCH_TYPE_HPP diff --git a/include/matador/utils/field_attributes.hpp b/include/matador/utils/field_attributes.hpp index 9736b2c..678e071 100644 --- a/include/matador/utils/field_attributes.hpp +++ b/include/matador/utils/field_attributes.hpp @@ -1,27 +1,72 @@ -#ifndef QUERY_FIELD_ATTRIBUTES_HPP -#define QUERY_FIELD_ATTRIBUTES_HPP +#ifndef MATADOR_FIELD_ATTRIBUTES_HPP +#define MATADOR_FIELD_ATTRIBUTES_HPP -#include "constraints.hpp" +#include "matador/utils/constraints.hpp" -#include +#include namespace matador::utils { +/** + * This class represents field attributes in + * form of size and constraints for a database + * field (column) + * + * Currently the size is only applied + * to a field of type string leading + * to VARCHAR(size). + */ class field_attributes { public: + /** + * Creates field_attributes instance + * with size 0 (zero) and no constraints. + */ field_attributes() = default; + /** + * Creates field_attributes instance + * with given size and no constraints. + * + * @param size Size of the attribute + */ field_attributes(size_t size); // NOLINT(*-explicit-constructor) + /** + * Creates field_attributes instance + * with size 0 (zero) and given constraints. + * + * @param options Constraints to apply to field + */ field_attributes(constraints options); // NOLINT(*-explicit-constructor) + /** + * Creates field_attributes instance + * with given size and constraints. + * + * @param size Size of the attribute + * @param options Constraints to apply to field + */ field_attributes(size_t size, constraints options); - field_attributes(const field_attributes &x) = default; - field_attributes& operator=(const field_attributes &x) = default; - field_attributes(field_attributes &&x) = default; - field_attributes& operator=(field_attributes &&x) = default; ~field_attributes() = default; + field_attributes(const field_attributes &) = default; + field_attributes(field_attributes &&) = default; + field_attributes &operator=(const field_attributes &) = default; + field_attributes &operator=(field_attributes &&) = default; + + field_attributes& operator=(size_t size); + field_attributes& operator=(constraints opt); + /** + * Returns the size of the field + * + * @return Size of the field + */ [[nodiscard]] size_t size() const; - void size(size_t size); + + /** + * Returns the constraints of the field + * + * @return Constraints of the field + */ [[nodiscard]] constraints options() const; private: @@ -32,4 +77,4 @@ private: const field_attributes null_attributes {}; } -#endif //QUERY_FIELD_ATTRIBUTES_HPP +#endif //MATADOR_FIELD_ATTRIBUTES_HPP diff --git a/include/matador/utils/foreign_attributes.hpp b/include/matador/utils/foreign_attributes.hpp index b779c28..3798b3f 100644 --- a/include/matador/utils/foreign_attributes.hpp +++ b/include/matador/utils/foreign_attributes.hpp @@ -1,5 +1,5 @@ -#ifndef QUERY_FOREIGN_ATTRIBUTES_HPP -#define QUERY_FOREIGN_ATTRIBUTES_HPP +#ifndef MATADOR_FOREIGN_ATTRIBUTES_HPP +#define MATADOR_FOREIGN_ATTRIBUTES_HPP #include "matador/utils/fetch_type.hpp" #include "matador/utils/cascade_type.hpp" @@ -13,8 +13,8 @@ public: foreign_attributes(cascade_type cascade); // NOLINT(*-explicit-constructor) foreign_attributes(fetch_type fetch); // NOLINT(*-explicit-constructor) foreign_attributes(cascade_type cascade, fetch_type fetch); - foreign_attributes(const foreign_attributes &x) = default; - foreign_attributes& operator=(const foreign_attributes &x) = default; + foreign_attributes(const utils::foreign_attributes &x) = default; + foreign_attributes& operator=(const utils::foreign_attributes &x) = default; foreign_attributes(foreign_attributes &&x) = default; foreign_attributes& operator=(foreign_attributes &&x) = default; ~foreign_attributes() = default; @@ -27,8 +27,8 @@ private: fetch_type fetch_{fetch_type::LAZY}; }; -const foreign_attributes default_foreign_attributes {}; +const utils::foreign_attributes default_foreign_attributes {}; } -#endif //QUERY_FOREIGN_ATTRIBUTES_HPP +#endif //MATADOR_FOREIGN_ATTRIBUTES_HPP diff --git a/include/matador/utils/identifier.hpp b/include/matador/utils/identifier.hpp index 990e580..0847e6c 100644 --- a/include/matador/utils/identifier.hpp +++ b/include/matador/utils/identifier.hpp @@ -1,178 +1,177 @@ -#ifndef QUERY_IDENTIFIER_HPP -#define QUERY_IDENTIFIER_HPP +#ifndef MATADOR_IDENTIFIER_HPP +#define MATADOR_IDENTIFIER_HPP +#include "matador/utils/default_type_traits.hpp" #include "matador/utils/field_attributes.hpp" +#include "matador/utils/basic_types.hpp" +#include "matador/utils/types.hpp" #include #include #include -#include namespace matador::utils { -struct null_type_t {}; - -namespace detail { - -enum class identifier_type_t : unsigned int { - INTEGRAL_TYPE, - STRING_TYPE, - NULL_TYPE -}; - -template -struct identifier_type_traits; - -template -struct identifier_type_traits::value>::type> { - static identifier_type_t type() { return identifier_type_t::INTEGRAL_TYPE; } - static std::string type_string() { return "integral"; } - static bool is_valid(Type value) { return value > 0; } - static std::string to_string(Type value) { return std::to_string(value); } -}; - -template -struct identifier_type_traits::value>::type> { - static identifier_type_t type() { return identifier_type_t::STRING_TYPE; } - static std::string type_string() { return "string"; } - static bool is_valid(const Type &value) { return !value.empty(); } - static std::string to_string(Type value) { return value; } -}; - -template -struct identifier_type_traits::value>::type> { - static identifier_type_t type() { return identifier_type_t::NULL_TYPE; } - static std::string type_string() { return "null"; } - static bool is_valid() { return false; } - static std::string to_string() { return "null_pk"; } -}; - -} - class identifier_serializer { public: virtual ~identifier_serializer() = default; - virtual void serialize(short &, const field_attributes &) = 0; - virtual void serialize(int &, const field_attributes &) = 0; - virtual void serialize(long &, const field_attributes &) = 0; - virtual void serialize(long long &, const field_attributes &) = 0; - virtual void serialize(unsigned short &, const field_attributes &) = 0; - virtual void serialize(unsigned int &, const field_attributes &) = 0; - virtual void serialize(unsigned long &, const field_attributes &) = 0; - virtual void serialize(unsigned long long &, const field_attributes &) = 0; + virtual void serialize(int8_t &, const field_attributes &) = 0; + virtual void serialize(int16_t &, const field_attributes &) = 0; + virtual void serialize(int32_t &, const field_attributes &) = 0; + virtual void serialize(int64_t &, const field_attributes &) = 0; + virtual void serialize(uint8_t &, const field_attributes &) = 0; + virtual void serialize(uint16_t &, const field_attributes &) = 0; + virtual void serialize(uint32_t &, const field_attributes &) = 0; + virtual void serialize(uint64_t &, const field_attributes &) = 0; + virtual void serialize(const char*, const field_attributes &) = 0; virtual void serialize(std::string &, const field_attributes &) = 0; virtual void serialize(null_type_t &, const field_attributes &) = 0; }; +template +struct identifier_type_traits; + +template +struct identifier_type_traits>> { + static bool is_valid(Type value) { return value > 0; } + static std::string to_string(const Type value) { return std::to_string(value); } +}; + +template +struct identifier_type_traits>> { + static bool is_valid(const Type &value) { return !value.empty(); } + static std::string to_string(const Type &value) { return value; } +}; + +template<> +struct identifier_type_traits { + static bool is_valid() { return false; } + static std::string to_string() { return "null"; } +}; + +template<> +struct identifier_type_traits { + static bool is_valid(const char *value); + static std::string to_string(const char *value); +}; + +namespace detail { + +template +size_t hash(const Type &value) { + return std::hash()(value); +} + +size_t hash(const char *value); + +} + class identifier { private: struct base { - explicit base(const std::type_index &ti, detail::identifier_type_t id_type); + base(const std::type_index &ti, basic_type type); base(const base &x) = delete; base &operator=(const base &x) = delete; base(base &&x) = delete; base &operator=(base &&x) = delete; virtual ~base() = default; - template - bool is_similar_type() const - { - return identifier_type_ == detail::identifier_type_traits::type(); - } - - bool is_similar_type(const base &x) const; - detail::identifier_type_t type() const; - - virtual base *copy() const = 0; - virtual bool equal_to(const base &x) const = 0; - virtual bool less(const base &x) const = 0; - virtual bool is_valid() const = 0; + [[nodiscard]] virtual base *copy() const = 0; + [[nodiscard]] virtual bool equal_to(const base &x) const = 0; + [[nodiscard]] virtual bool less(const base &x) const = 0; + [[nodiscard]] virtual bool is_valid() const = 0; virtual void serialize(identifier_serializer &s) = 0; - virtual std::string str() const = 0; - virtual size_t hash() const = 0; + [[nodiscard]] virtual std::string str() const = 0; + [[nodiscard]] virtual size_t hash() const = 0; std::type_index type_index_; - detail::identifier_type_t identifier_type_; + basic_type type_{basic_type::type_null}; }; template - struct pk : public base + struct pk final : base { - using self = pk; + using self = pk; - explicit pk(const IdType &id, size_t size = 0) : base(std::type_index(typeid(IdType)), detail::identifier_type_traits::type()) - , id_(id) - , size_(size) {} + explicit pk(const IdType &id) + : base(std::type_index(typeid(IdType)), data_type_traits::type(1)) + , id_(id) + , size_(sizeof(IdType)) {} - base *copy() const final { - return new self(id_, size_); + [[nodiscard]] base *copy() const override { + return new self(id_); } - bool equal_to(const base &x) const final { - return static_cast &>(x).id_ == id_; + [[nodiscard]] bool equal_to(const base &x) const override { + return static_cast(x).id_ == id_; } - bool less(const base &x) const final { - return static_cast &>(x).id_ < id_; + [[nodiscard]] bool less(const base &x) const override { + return id_ < static_cast(x).id_; } - bool is_valid() const final - { - return detail::identifier_type_traits::is_valid(id_); + [[nodiscard]] bool is_valid() const override { + return identifier_type_traits::is_valid(id_); } - std::string str() const final - { - return detail::identifier_type_traits::to_string(id_); + [[nodiscard]] std::string str() const override { + return identifier_type_traits::to_string(id_); } - void serialize(identifier_serializer &s) final { + void serialize(identifier_serializer &s) override { s.serialize(id_, size_); } - size_t hash() const final { - std::hash hash_func; - return hash_func(id_); + [[nodiscard]] size_t hash() const override { + return detail::hash(id_); } IdType id_; size_t size_{}; }; - struct null_pk : public base + struct null_pk final : base { null_pk(); - base *copy() const final; - bool equal_to(const base &x) const final; - bool less(const base &x) const final; - bool is_valid() const final; - void serialize(identifier_serializer &s) final; - std::string str() const final; - size_t hash() const final; + [[nodiscard]] base *copy() const override; + [[nodiscard]] bool equal_to(const base &x) const override; + [[nodiscard]] bool less(const base &x) const override; + [[nodiscard]] bool is_valid() const override; + void serialize(identifier_serializer &s) override; + [[nodiscard]] std::string str() const override; + [[nodiscard]] size_t hash() const override; null_type_t null_; }; public: identifier(); template - explicit identifier(const Type &id, long size = -1) - : id_(std::make_shared>(id, size)) {} + explicit identifier(const Type &id) + : id_(std::make_shared>(id)) {} + explicit identifier(const char *id) + : id_(std::make_shared>(id)) {} identifier(const identifier &x); identifier &operator=(const identifier &x); identifier(identifier &&x) noexcept ; identifier &operator=(identifier &&x) noexcept; template - identifier &operator=(const Type &value) + identifier& operator=(const Type &value) { id_ = std::make_shared>(value); return *this; } + identifier& operator=(const char *value) + { + id_ = std::make_shared>(value); + return *this; + } + ~identifier() = default; bool operator==(const identifier &x) const; @@ -182,26 +181,28 @@ public: bool operator>(const identifier &x) const; bool operator>=(const identifier &x) const; - bool is_similar_type(const identifier &x) const; - template - bool is_similar_type() const - { - return id_->is_similar_type(); - } + [[nodiscard]] std::string str() const; + [[nodiscard]] const std::type_index &type_index() const; + [[nodiscard]] basic_type type() const; - std::string str() const; - const std::type_index &type_index() const; + [[nodiscard]] identifier share() const; + [[nodiscard]] size_t use_count() const; - identifier share() const; - size_t use_count() const; + [[nodiscard]] bool is_integer() const; + [[nodiscard]] bool is_floating_point() const; + [[nodiscard]] bool is_bool() const; + [[nodiscard]] bool is_varchar() const; + [[nodiscard]] bool is_date() const; + [[nodiscard]] bool is_time() const; + [[nodiscard]] bool is_blob() const; + [[nodiscard]] bool is_null() const; - bool is_null() const; - bool is_valid() const; + [[nodiscard]] bool is_valid() const; void clear(); - void serialize(identifier_serializer &s); + void serialize(identifier_serializer &s) const; - size_t hash() const; + [[nodiscard]] size_t hash() const; friend std::ostream &operator<<(std::ostream &out, const identifier &id); @@ -214,10 +215,12 @@ private: static identifier null_identifier{}; +/// @cond MATADOR_DEV struct id_pk_hash { size_t operator()(const identifier &id) const; }; - +/// @endcond } -#endif //QUERY_IDENTIFIER_HPP + +#endif //MATADOR_IDENTIFIER_HPP diff --git a/include/matador/utils/library.hpp b/include/matador/utils/library.hpp index b13e134..fd8ce18 100644 --- a/include/matador/utils/library.hpp +++ b/include/matador/utils/library.hpp @@ -1,5 +1,7 @@ -#ifndef QUERY_LIBRARY_HPP -#define QUERY_LIBRARY_HPP +#ifndef LIBRARY_HPP +#define LIBRARY_HPP + +#include "matador/utils/export.hpp" #include @@ -16,7 +18,7 @@ namespace matador::utils { #if defined(_MSC_VER) || defined(__MINGW32__) typedef FARPROC func_ptr; #else -typedef void *func_ptr; +typedef void* func_ptr; #endif #endif /* MATADOR_DOXYGEN_DOC */ @@ -28,26 +30,24 @@ typedef void *func_ptr; * This class represents a loader and un-loader * for an external library. The path to the library * is given by a string. - * It can be loaded and unloaded. Furthermore it - * provides a method to get a pointer to an function + * It can be loaded and unloaded. Furthermore, it + * provides a method to get a pointer to a function * exported by the library. The function is identified * by a name. */ -class library +class MATADOR_UTILS_API library { public: /** * Create a unspecified library serializable */ library() = default; - /** * Create a library for the given lib path * * @param lib The path to the library to map. */ explicit library(std::string lib); - ~library(); /** @@ -73,7 +73,7 @@ public: bool load(const std::string &lib); /** - * Unload an loaded library. If + * Unload a loaded library. If * the library isn't loaded or an * error occurred while unloading * false is returned. @@ -104,4 +104,4 @@ private: } -#endif //QUERY_LIBRARY_HPP +#endif /* LIBRARY_HPP */ diff --git a/include/matador/utils/logger.hpp b/include/matador/utils/logger.hpp deleted file mode 100644 index 86e1217..0000000 --- a/include/matador/utils/logger.hpp +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef QUERY_LOGGER_HPP -#define QUERY_LOGGER_HPP - -#include -#include - -namespace matador::utils { - -enum class log_level -{ - LVL_FATAL, /**< If a serious error occurred use FATAL level */ - LVL_ERROR, /**< On error use ERROR level */ - LVL_WARN, /**< Warnings should use WARN level */ - LVL_INFO, /**< Information should go with INFO level */ - LVL_DEBUG, /**< Debug output should use DEBUG level */ - LVL_TRACE, /**< Trace information should use TRACE level */ - LVL_ALL /**< This level represents all log levels and should be used for logging */ -}; - -/** - * Simple file logger - */ -class logger -{ -public: - logger(const std::string &path, std::string source); - logger(FILE *file, std::string source); - logger(const logger &x); - logger& operator=(const logger &x); - logger(logger &&x) noexcept; - logger& operator=(logger &&x) noexcept; - - template - void info(const std::string &what, Args const &... args) const { info(what.c_str(), args...); } - template - void info(const char *what, Args const &... args) const { log(log_level::LVL_INFO, what, args...); } - - template - void debug(const std::string &what, Args const &... args) const { debug(what.c_str(), args...); } - template - void debug(const char *what, Args const &... args) const { log(log_level::LVL_DEBUG, what, args...); } - - template - void warn(const std::string &what, Args const &... args) const { warn(what.c_str(), args...); } - template - void warn(const char *what, Args const &... args) const { log(log_level::LVL_WARN, what, args...); } - - template - void error(const std::string &what, Args const &... args) const { error(what.c_str(), args...); } - template - void error(const char *what, Args const &... args) const { log(log_level::LVL_ERROR, what, args...); } - - template - void fatal(const std::string &what, Args const &... args) const { fatal(what.c_str(), args...); } - template - void fatal(const char *what, Args const &... args) const { log(log_level::LVL_FATAL, what, args...); } - - template - void log(log_level lvl, const char *what, Args const &... args) const; - void log(log_level lvl, const char *message) const; - -private: - void write(const char *message, size_t size) const; - void close(); - -private: - std::string path_; - FILE *stream = nullptr; - mutable std::mutex mutex_; - std::string source_; -}; - -template -void logger::log(log_level lvl, const char *what, ARGS const &... args) const -{ - char message_buffer[16384]; - -#ifdef _MSC_VER - sprintf_s(message_buffer, 16384, what, args...); -#else - sprintf(message_buffer, what, args...); -#endif - - log(lvl, message_buffer); -} - -} -#endif //QUERY_LOGGER_HPP diff --git a/include/matador/utils/macro_map.hpp b/include/matador/utils/macro_map.hpp deleted file mode 100644 index dee5f31..0000000 --- a/include/matador/utils/macro_map.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef QUERY_MACRO_MAP_HPP -#define QUERY_MACRO_MAP_HPP - -#define EVAL0(...) __VA_ARGS__ -#define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__))) -#define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) -#define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) -#define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) -#define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) - -#define MAP_END(...) -#define MAP_OUT -#define MAP_COMMA , - -#define MAP_GET_END2() 0, MAP_END -#define MAP_GET_END1(...) MAP_GET_END2 -#define MAP_GET_END(...) MAP_GET_END1 -#define MAP_NEXT0(test, next, ...) next MAP_OUT -#define MAP_NEXT1(test, next) MAP_NEXT0(test, next, 0) -#define MAP_NEXT(test, next) MAP_NEXT1(MAP_GET_END test, next) - -#define MAP0(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP1)(f, peek, __VA_ARGS__) -#define MAP1(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP0)(f, peek, __VA_ARGS__) - -#define MAP_LIST_NEXT1(test, next) MAP_NEXT0(test, MAP_COMMA next, 0) -#define MAP_LIST_NEXT(test, next) MAP_LIST_NEXT1(MAP_GET_END test, next) - -#define MAP_LIST0(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST1)(f, peek, __VA_ARGS__) -#define MAP_LIST1(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST0)(f, peek, __VA_ARGS__) - -/** - * Applies the function macro `f` to each of the remaining parameters. - */ -#define MAP(f, ...) EVAL(MAP1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) - -/** - * Applies the function macro `f` to each of the remaining parameters and - * inserts commas between the results. - */ -#define MAP_LIST(f, ...) EVAL(MAP_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) - -#endif //QUERY_MACRO_MAP_HPP diff --git a/include/matador/utils/os.hpp b/include/matador/utils/os.hpp index b2c3130..b3e0365 100644 --- a/include/matador/utils/os.hpp +++ b/include/matador/utils/os.hpp @@ -1,44 +1,105 @@ -#ifndef QUERY_OS_HPP -#define QUERY_OS_HPP +#ifndef MATADOR_OS_HPP +#define MATADOR_OS_HPP +#include "matador/utils/export.hpp" + +#include #include namespace matador::utils::os { +enum class override_env_value { + KeepValue, + OverrideValue +}; #ifdef _WIN32 -extern char DIR_SEPARATOR; -extern const char* DIR_SEPARATOR_STRING; -#else -extern char DIR_SEPARATOR; -extern const char* DIR_SEPARATOR_STRING; +std::string error_string(unsigned long error); #endif -std::string error_string(unsigned long error); - +void setenv(const char *name, const char *value, override_env_value override_value); std::string getenv(const char* name); +void unsetenv(const char *name); +} -[[maybe_unused]] std::string getenv(const std::string &name); +namespace matador::os { -FILE* fopen(const std::string &path, const char *modes); -FILE* fopen(const char *path, const char *modes); +/// @cond MATADOR_DEV -std::string get_current_dir(); +#ifdef _WIN32 +MATADOR_UTILS_API extern char DIR_SEPARATOR; +MATADOR_UTILS_API extern const char* DIR_SEPARATOR_STRING; +#else +MATADOR_UTILS_API extern char DIR_SEPARATOR; +MATADOR_UTILS_API extern const char* DIR_SEPARATOR_STRING; +#endif -bool mkdir(const std::string &dirname); -bool mkdir(const char *dirname); +MATADOR_UTILS_API FILE* fopen(const std::string &path, const char *modes); +MATADOR_UTILS_API FILE* fopen(const char *path, const char *modes); -bool chdir(const std::string &dirname); -bool chdir(const char *dirname); +MATADOR_UTILS_API FILE* freopen(const std::string &path, const char *modes, FILE *stream); +MATADOR_UTILS_API FILE* freopen(const char *path, const char *modes, FILE *stream); -bool rmdir(const std::string &dirname); -bool rmdir(const char *dirname); +MATADOR_UTILS_API bool fclose(FILE *f); -bool mkpath(const std::string &path); -bool mkpath(const char *path); +MATADOR_UTILS_API bool remove(const std::string &name); +MATADOR_UTILS_API bool remove(const char *name); -bool rmpath(const std::string &path); -bool rmpath(const char *path); +MATADOR_UTILS_API bool rename(const std::string &old_name, const std::string &new_name); +MATADOR_UTILS_API bool rename(const char *old_name, const char *new_name); + +MATADOR_UTILS_API bool access(const std::string &path, int mode); +MATADOR_UTILS_API bool access(const char *path, int mode); + +MATADOR_UTILS_API int dup(FILE *stream); + +MATADOR_UTILS_API bool mkdir(const std::string &dirname); +MATADOR_UTILS_API bool mkdir(const char *dirname); + +MATADOR_UTILS_API bool chdir(const std::string &dirname); +MATADOR_UTILS_API bool chdir(const char *dirname); + +MATADOR_UTILS_API bool rmdir(const std::string &dirname); +MATADOR_UTILS_API bool rmdir(const char *dirname); + +MATADOR_UTILS_API std::string get_current_dir(); + +MATADOR_UTILS_API bool mkpath(const std::string &path); +MATADOR_UTILS_API bool mkpath(const char *path); + +MATADOR_UTILS_API bool rmpath(const std::string &path); +MATADOR_UTILS_API bool rmpath(const char *path); + +MATADOR_UTILS_API bool is_readable(const std::string &path); +MATADOR_UTILS_API bool is_readable(const char *path); +MATADOR_UTILS_API bool is_writable(const std::string &path); +MATADOR_UTILS_API bool is_writable(const char *path); +MATADOR_UTILS_API bool exists(const std::string &path); +MATADOR_UTILS_API bool exists(const char *path); + +MATADOR_UTILS_API size_t file_size(FILE *stream); + +MATADOR_UTILS_API std::string build_path(const std::string &a, const std::string &b); + +template +std::string build_path(const std::string &a, const std::string &b, T& ...arg) +{ + return build_path(build_path(a, b), arg...); +} + +template +int sprintf(char* str, size_t s, const char* format, ARGS const&... args) +{ +#ifdef _WIN32 + return sprintf_s(str, s, format, args...); +#else + return ::snprintf(str, s, format, args...); +#endif +} + +MATADOR_UTILS_API char* strerror(int err, char* errbuf, size_t bufsize); + +/// @endcond } -#endif //QUERY_OS_HPP +#endif //MATADOR_OS_HPP diff --git a/include/matador/sql/placeholder.hpp b/include/matador/utils/placeholder.hpp similarity index 78% rename from include/matador/sql/placeholder.hpp rename to include/matador/utils/placeholder.hpp index ab9dce8..2447c0a 100644 --- a/include/matador/sql/placeholder.hpp +++ b/include/matador/utils/placeholder.hpp @@ -1,13 +1,13 @@ #ifndef QUERY_PLACEHOLDER_HPP #define QUERY_PLACEHOLDER_HPP -namespace matador::sql { +namespace matador::utils { struct placeholder {}; inline constexpr bool operator==(const placeholder&, const placeholder&) { return true; } -const placeholder _; +static constexpr placeholder _; } diff --git a/include/matador/utils/result.hpp b/include/matador/utils/result.hpp index 0776e61..59441ab 100644 --- a/include/matador/utils/result.hpp +++ b/include/matador/utils/result.hpp @@ -2,6 +2,9 @@ #define QUERY_RESULT_HPP #include +#include +#include +#include namespace matador::utils { @@ -30,14 +33,22 @@ private: ValueType value_; }; +template <> +class ok +{ +public: + using value_type = void; + explicit constexpr ok() = default; +}; + template < typename ErrorType > -class error +class failure { public: using value_type = ErrorType; - explicit constexpr error(const ErrorType &error) : error_(error) {} - explicit constexpr error(ErrorType &&error) : error_(std::move(error)) {} + explicit constexpr failure(const ErrorType &error) : error_(error) {} + explicit constexpr failure(ErrorType &&error) : error_(std::move(error)) {} constexpr ErrorType&& release() { return std::move(error_); } const ErrorType& value() const { return error_; } @@ -51,51 +62,68 @@ template < typename ValueType, typename ErrorType > class result { public: - using value_type = ok; - using error_type = error; + using value_type = ValueType; + using error_type = ErrorType; result() : result_(ValueType{}) {} - result(value_type value) : result_(std::move(value)) {} // NOLINT(*-explicit-constructor) - result(error_type error) : result_(std::move(error)) {} // NOLINT(*-explicit-constructor) - result(const result &x) = default; - result& operator=(const result &x) = default; - result(result &&x) = default; - result& operator=(result &&x) = default; + result(ok value) : result_(std::move(value.release())) {} // NOLINT(*-explicit-constructor) + result(failure error) : result_(std::move(error.release())) {} // NOLINT(*-explicit-constructor) + result(const result &x) = default; + result& operator=(const result &x) = default; + result(result &&x) = default; + result& operator=(result &&x) = default; operator bool() const { return is_ok(); } // NOLINT(*-explicit-constructor) [[nodiscard]] bool is_ok() const { return std::holds_alternative(result_); } [[nodiscard]] bool is_error() const { return std::holds_alternative(result_); } - ValueType&& release() { return std::get(result_).release(); } - ErrorType&& release_error() { return std::get(result_).release(); } + ValueType&& release() { return std::move(std::get(result_)); } + ErrorType&& release_error() { return std::move(std::get(result_)); } - const ValueType& value() const { return std::get(result_).value(); } - const ErrorType& err() const { return std::get(result_).value(); } - ErrorType err() { return std::get(result_).value(); } + const ValueType& value() const { return std::get(result_); } + ValueType& value() { return std::get(result_); } + const ErrorType& err() const { return std::get(result_); } + ErrorType err() { return std::get(result_); } constexpr const ValueType* operator->() const { return &value(); } - constexpr ValueType* operator->() { return &std::get(result_).value(); } + constexpr ValueType* operator->() { return &std::get(result_); } - template> - result transform(Func &&f) { + constexpr const ValueType& operator*() const& noexcept { return value(); } + constexpr ValueType& operator*() & noexcept { return value(); } + + template> + result map(Func &&f) { if (is_ok()) { return result(ok(f(release()))); } - return result(error(release_error())); + return result(failure(release_error())); } - template::value_type::value_type> + template::value_type> + result map_error(Func &&f) { + if (!is_ok()) { + return result(ok(release())); + } + + return result(error(release_error())); + } + + template::value_type> result and_then(Func &&f) { if (is_ok()) { return f(release()); } - return result(error(release_error())); + return result(failure(release_error())); } - template::error_type::value_type> + template::value_type> result or_else(Func &&f) { if (is_error()) { return f(err()); @@ -108,6 +136,62 @@ private: std::variant result_; }; +template < typename ErrorType > +class result +{ +public: + using value_type = void; + using error_type = ErrorType; + + result() = default; + result(ok /*value*/) {} + result(failure error) : result_(std::move(error.release())) {} // NOLINT(*-explicit-constructor) + result(const result &x) = default; + result& operator=(const result &x) = default; + result(result &&x) = default; + result& operator=(result &&x) = default; + + operator bool() const { return is_ok(); } // NOLINT(*-explicit-constructor) + + [[nodiscard]] bool is_ok() const { return !result_.has_value(); } + [[nodiscard]] bool is_error() const { return result_.has_value(); } + + ErrorType&& release_error() { return result_->release(); } + + const ErrorType& err() const { return result_.value(); } + ErrorType err() { return result_.value(); } + + template> + result map(Func &&f) { + if (is_ok()) { + return result(ok(f())); + } + + return result(error(release_error())); + } + + template + result and_then(Func &&f) { + if (is_ok()) { + return f(); + } + + return result(error(release_error())); + } + + template::value_type> + result or_else(Func &&f) { + if (is_error()) { + return f(err()); + } + + return result(ok()); + } + +private: + std::optional result_; +}; + } #endif //QUERY_RESULT_HPP diff --git a/include/matador/utils/singleton.hpp b/include/matador/utils/singleton.hpp new file mode 100644 index 0000000..91983c4 --- /dev/null +++ b/include/matador/utils/singleton.hpp @@ -0,0 +1,45 @@ +#ifndef SINGLETON_HPP +#define SINGLETON_HPP + +namespace matador::utils { + +/** + * @class singleton + * @brief Provides only one instance of a type. + * @tparam T The type of the singleton. + * + * This class implements the singleton pattern. + * It ensures that only one instance of a class + * exists. + * @note The derived class must add a friend declaration + * for the concrete singleton type. + */ +template < typename T > +class singleton +{ +public: + typedef T value_type; /**< Shortcut for the singletons type */ + + /** + * @brief Access the instance of the class. + * + * The static instance method provides + * the access to the one instance of the + * concrete class. + * + * @return The one instance of the class. + */ + static value_type& instance () + { + static value_type instance_; + return instance_; + } + virtual ~singleton() = default; + +protected: + singleton() = default; +}; + +} + +#endif /* SINGLETON_HPP */ diff --git a/include/matador/utils/string.hpp b/include/matador/utils/string.hpp index 9f2f704..a4301a4 100644 --- a/include/matador/utils/string.hpp +++ b/include/matador/utils/string.hpp @@ -1,13 +1,37 @@ -#ifndef QUERY_STRING_HPP -#define QUERY_STRING_HPP +#ifndef STRING_HPP +#define STRING_HPP +#include "matador/utils/convert.hpp" +#include "matador/utils/export.hpp" #include "matador/utils/types.hpp" +#include #include -#include namespace matador::utils { +/** + * Converts each byte of the given binary data + * into is hex string representation and return + * all bytes of blob as string. + * + * @param data Binary data to be converted + * @return Binary data as string + */ +MATADOR_UTILS_API std::string to_string(const blob &data); + +/** + * Splits a string by a delimiter and + * add the string tokens to a vector. The + * size of the vector is returned. + * + * @param str The string to split. + * @param delim The delimiter character. + * @param values The result vector. + * @return The size of the vector. + */ +MATADOR_UTILS_API size_t split(const std::string &str, char delim, std::vector &values); + /** * Splits a string by a delimiter and * add the string tokens to a vector. The @@ -15,9 +39,32 @@ namespace matador::utils { * * @param str The string to split. * @param delim The delimiter character. - * @return The the vector with split strings. + * @return The vector with split strings. */ -std::vector split(const std::string &str, char delim); +MATADOR_UTILS_API std::vector split(const std::string &str, char delim); + +/** + * Splits a string by a delimiter and + * add the string tokens to a list. The + * size of the list is returned. + * + * @param str The string to split. + * @param delim The delimiter character. + * @param values The result list. + * @return The size of the list. + */ +MATADOR_UTILS_API size_t split(const std::string &str, char delim, std::list &values); + +/** + * @fn std::string trim(const std::string& str, const std::string&) + * Trims a string by removing leading and trailing characters + * The default characters are spaces and tabs + * + * @param str The string to be trimmed + * @param whitespace The trimming characters + * @return the trimmed string + */ +MATADOR_UTILS_API std::string trim(const std::string& str, const std::string& whitespace = " \t"); /** * Replaces all occurrences of string from in given string @@ -27,23 +74,28 @@ std::vector split(const std::string &str, char delim); * @param from The string to be replaced * @param to The new string */ -void replace_all(std::string &in, const std::string &from, const std::string &to); - -const std::string& to_string(const std::string &str); -std::string to_string(const blob &data); +MATADOR_UTILS_API void replace_all(std::string &in, const std::string &from, const std::string &to); +template +std::string to_string(const Type &value) { + const auto res = to(value); + if (res.is_ok()) { + return *res; + } + return ""; +} /** * Joins a range of elements as string within a list * with a given delimiter and writes it to the * given stream * - * @tparam R Type og the range (e.g. map, list, vector, etc) + * @tparam Range Type og the range (e.g. map, list, vector, etc) * @param range The range with the elements to join_left * @param delim The delimiter for the elements * @return The ostream reference */ -template < class R > -std::string join(R &range, const std::string &delim) +template < class Range > +std::string join(const Range &range, const std::string &delim) { std::string result {}; if (range.size() < 2) { @@ -61,4 +113,4 @@ std::string join(R &range, const std::string &delim) } } -#endif //QUERY_STRING_HPP +#endif //STRING_HPP diff --git a/include/matador/utils/types.hpp b/include/matador/utils/types.hpp index 4a239a3..30202e6 100644 --- a/include/matador/utils/types.hpp +++ b/include/matador/utils/types.hpp @@ -1,13 +1,31 @@ -#ifndef QUERY_UTILS_TYPES_HPP -#define QUERY_UTILS_TYPES_HPP +#ifndef MATADOR_TYPES_HPP +#define MATADOR_TYPES_HPP +#include #include +#include +#include namespace matador::utils { +enum class basic_type : uint8_t; + using byte = unsigned char; using blob = std::vector; +using database_type = std::variant< + uint8_t, uint16_t, uint32_t, uint64_t, + int8_t, int16_t, int32_t, int64_t, + float, double, + bool, + std::string, + blob, + nullptr_t>; + +struct null_type_t {}; + +void initialize_by_basic_type(basic_type type, database_type &val); + } -#endif //QUERY_UTILS_TYPES_HPP +#endif //MATADOR_TYPES_HPP diff --git a/include/matador/sql/value.hpp b/include/matador/utils/value.hpp similarity index 55% rename from include/matador/sql/value.hpp rename to include/matador/utils/value.hpp index c3ff003..0194bd3 100644 --- a/include/matador/sql/value.hpp +++ b/include/matador/utils/value.hpp @@ -1,15 +1,13 @@ #ifndef QUERY_VALUE_HPP #define QUERY_VALUE_HPP -#include "matador/sql/any_type.hpp" -#include "matador/sql/any_type_to_visitor.hpp" -#include "matador/sql/data_type_traits.hpp" - +#include "matador/utils/basic_type_converter.hpp" +#include "matador/utils/default_type_traits.hpp" #include "matador/utils/types.hpp" #include -namespace matador::sql { +namespace matador::utils { namespace detail { template @@ -19,61 +17,72 @@ size_t determine_size(const Type &/*val*/) } size_t determine_size(const std::string &val); size_t determine_size(const char *val); -size_t determine_size(const utils::blob &val); +size_t determine_size(const blob &val); } + class value { public: value() = default; template explicit value(Type value, size_t size = 0) - : size_(size) - , value_(value) - , type_(data_type_traits::builtin_type(size)) {} - explicit value(data_type_t data_type, size_t size = 0); + : value_(value) + , size_(size) + , type_(data_type_traits::type(size)) {} + explicit value(basic_type data_type, size_t size = 0); value(const value &x) = default; value& operator=(const value &x) = default; template - value& operator=(Type val) - { + value& operator=(Type val) { value_ = val; size_ = detail::determine_size(val); - type_ = data_type_traits::builtin_type(size_); + type_ = data_type_traits::type(size_); return *this; } value(value &&x) noexcept; value& operator=(value &&x) noexcept; template - std::optional as() const - { + std::optional as() const { if (std::holds_alternative(value_)) { return std::get(value_); - } else { - any_type_to_visitor visitor; - std::visit(visitor, const_cast(value_)); - return visitor.result; } + const auto res = basic_type_converter::convert_value(value_); + if (!res.is_ok()) { + return std::nullopt; + } + return *res; } + + template + std::optional> ref() { + if (std::holds_alternative(value_)) { + return std::get(value_); + } + + return std::nullopt; + } + [[nodiscard]] std::string str() const; [[nodiscard]] size_t size() const; - [[nodiscard]] data_type_t type() const; + [[nodiscard]] basic_type type() const; [[nodiscard]] bool is_integer() const; [[nodiscard]] bool is_floating_point() const; [[nodiscard]] bool is_bool() const; [[nodiscard]] bool is_string() const; [[nodiscard]] bool is_varchar() const; + [[nodiscard]] bool is_date() const; + [[nodiscard]] bool is_time() const; [[nodiscard]] bool is_blob() const; [[nodiscard]] bool is_null() const; - [[nodiscard]] bool is_unknown() const; private: - any_type value_; + utils::database_type value_; size_t size_{}; - data_type_t type_{data_type_t::type_unknown}; + basic_type type_{basic_type::type_null}; }; diff --git a/include/matador/utils/version.hpp b/include/matador/utils/version.hpp new file mode 100644 index 0000000..644d87c --- /dev/null +++ b/include/matador/utils/version.hpp @@ -0,0 +1,50 @@ +#ifndef MATADOR_VERSION_HPP +#define MATADOR_VERSION_HPP + +#include "matador/utils/result.hpp" +#include "matador/utils/error.hpp" + +#include +#include + +namespace matador::utils { + +class version { +public: + version() = default; + ~version() =default; + version(unsigned int major, unsigned int minor, unsigned int patch); + version(const version& x) = default; + version& operator=(const version& x) = default; + version(version&& x) noexcept = default; + version& operator=(version&& x) noexcept = default; + + bool operator==(const version &x) const; + bool operator!=(const version &x) const; + bool operator>(const version &x) const; + bool operator>=(const version &x) const; + bool operator<(const version &x) const; + bool operator<=(const version &x) const; + + [[nodiscard]] std::string str() const; + friend std::ostream& operator<<(std::ostream &out, const version &v); + + static result from_string(const std::string &version_string); + + [[nodiscard]] unsigned int major() const; + [[nodiscard]] unsigned int minor() const; + [[nodiscard]] unsigned int patch() const; + + void major(unsigned int m); + void minor(unsigned int m); + void patch(unsigned int p); + +private: + unsigned int major_{}; + unsigned int minor_{}; + unsigned int patch_{}; +}; + +} + +#endif //MATADOR_VERSION_HPP diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt new file mode 100644 index 0000000..c573d24 --- /dev/null +++ b/source/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(core) +add_subdirectory(orm) \ No newline at end of file diff --git a/source/core/CMakeLists.txt b/source/core/CMakeLists.txt new file mode 100644 index 0000000..1e05046 --- /dev/null +++ b/source/core/CMakeLists.txt @@ -0,0 +1,57 @@ +add_library(matador-core STATIC + ../../include/matador/utils/access.hpp + ../../include/matador/utils/attribute_reader.hpp + ../../include/matador/utils/attribute_writer.hpp + ../../include/matador/utils/base_class.hpp + ../../include/matador/utils/basic_type_converter.hpp + ../../include/matador/utils/basic_types.hpp + ../../include/matador/utils/cascade_type.hpp + ../../include/matador/utils/constraints.hpp + ../../include/matador/utils/convert.hpp + ../../include/matador/utils/data_type_traits.hpp + ../../include/matador/utils/default_type_traits.hpp + ../../include/matador/utils/di.hpp + ../../include/matador/utils/enum_mapper.hpp + ../../include/matador/utils/error.hpp + ../../include/matador/utils/errors.hpp + ../../include/matador/utils/export.hpp + ../../include/matador/utils/fetch_type.hpp + ../../include/matador/utils/field_attributes.hpp + ../../include/matador/utils/foreign_attributes.hpp + ../../include/matador/utils/identifier.hpp + ../../include/matador/utils/library.hpp + ../../include/matador/utils/os.hpp + ../../include/matador/utils/placeholder.hpp + ../../include/matador/utils/result.hpp + ../../include/matador/utils/singleton.hpp + ../../include/matador/utils/string.hpp + ../../include/matador/utils/types.hpp + ../../include/matador/utils/value.hpp + ../../include/matador/utils/version.hpp + utils/default_type_traits.cpp + utils/error.cpp + utils/field_attributes.cpp + utils/identifier.cpp + utils/library.cpp + utils/os.cpp + utils/string.cpp + utils/types.cpp + utils/value.cpp + utils/version.cpp + utils/errors.cpp + ../../include/matador/object/basic_object_info.hpp + ../../include/matador/object/error_code.hpp + ../../include/matador/object/schema.hpp + ../../include/matador/object/schema_node.hpp + object/error_code.cpp + object/schema.cpp + object/schema_node.cpp + object/basic_object_info.cpp +) + +target_link_libraries(matador-core ${CMAKE_DL_LIBS}) + +target_include_directories(matador-core + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/../../include +) diff --git a/source/core/object/basic_object_info.cpp b/source/core/object/basic_object_info.cpp new file mode 100644 index 0000000..7158b5b --- /dev/null +++ b/source/core/object/basic_object_info.cpp @@ -0,0 +1,13 @@ +#include "matador/object/basic_object_info.hpp" + +#include + +namespace matador::object { + +basic_object_info::basic_object_info(schema_node &node, const std::type_index type_index) +: node_(node) +, type_index_(type_index) {} + +std::type_index basic_object_info::type_index() const { return type_index_; } + +} // namespace matador::object diff --git a/source/core/object/error_code.cpp b/source/core/object/error_code.cpp new file mode 100644 index 0000000..fc393f9 --- /dev/null +++ b/source/core/object/error_code.cpp @@ -0,0 +1,37 @@ +#include "matador/object/error_code.hpp" + +namespace matador::object { + +const char * object_category_impl::name() const noexcept { + return "sql"; +} + +std::string object_category_impl::message(const int ev) const { + switch (static_cast(ev)) { + case error_code::OK: + return "OK"; + case error_code::NodeNotFound: + return "Node not found"; + case error_code::NodeAlreadyExists: + return "Node already exists"; + case error_code::Failure: + return "Failure"; + default: + return "Unknown error"; + } +} + +const std::error_category & object_category() { + static object_category_impl instance; + return instance; +} + +std::error_code make_error_code(error_code e) { + return {static_cast(e), object_category()}; +} + +std::error_condition make_error_condition(error_code e) { + return {static_cast(e), object_category()}; +} + +} \ No newline at end of file diff --git a/source/core/object/schema.cpp b/source/core/object/schema.cpp new file mode 100644 index 0000000..5eb186d --- /dev/null +++ b/source/core/object/schema.cpp @@ -0,0 +1,98 @@ +#include "matador/object/schema.hpp" + +namespace matador::object { + +utils::error make_error(const error_code ec, const std::string& msg) { + return utils::error(ec, msg); +} + +schema::schema() +: root_(std::shared_ptr(new schema_node(*this))) { + root_->first_child_ = std::shared_ptr(new schema_node(*this)); + root_->last_child_ = std::shared_ptr(new schema_node(*this)); + root_->first_child_->next_sibling_ = root_->last_child_; + root_->last_child_->previous_sibling_ = root_->first_child_; +} + +bool schema::empty() const { + return root_->first_child_ == root_->last_child_->previous_sibling_; +} + +size_t schema::size() const { return 0; } + +utils::result, utils::error> schema::attach_node(const std::shared_ptr &node, + const std::string &parent) { + if (!has_node(node->type_index(), node->name())) { + return utils::failure(make_error(error_code::NodeAlreadyExists, "Node '" + node->name() + "' already exists.")); + } + + // set node to root node + auto parent_node = root_; + auto result = find_parent(parent); + if (!result.is_ok() && result.err().ec() != error_code::NodeNotFound) { + return result; + } + parent_node = *result; + + result = push_back_child(root_, node); + + // if (!pk.is_null()) { + // node->primary_key_ = pk; + // } + // store prototype in map + // Todo: check return value + node_map_.insert(std::make_pair(node->name(), node))/*.first*/; + type_index_node_map_[node->type_index()].insert(std::make_pair(node->name(), node)); + + // return nptr.release(); + return {}; +} + +utils::result, utils::error> schema::find_parent(const std::string &name) const { + if (name.empty()) { + return utils::failure(make_error(error_code::Failure, "Name of parent cannot be empty")); + } + + return find_node(name); +} + +utils::result, utils::error> schema::find_node(const std::string &name) const { + // first search in the prototype map + const auto i = node_map_.find(name); + if (i == node_map_.end()) { + // if not found search in the typeid to prototype map + // const auto j = type_index_node_map_.find(name); + // if (j == type_index_node_map_.end()) { + // return utils::failure( + // make_error(error_code::NodeNotFound, std::string("Could not find node with name '") + name + "'")); + // } + // const t_node_map &val = j->second; + /* + * if size is greater one (1) the name + * is a typeid and has more than one prototype + * node and therefor it is not unique and an + * exception is thrown + */ + // if (val.size() > 1) { + // return utils::failure(make_error(error_code::NodeNotFound, std::string("Type id '") + name + "' is not unique")); + // } + // // return the only prototype + // return utils::ok(val.begin()->second); + } + return utils::ok(i->second); +} +utils::result, utils::error> schema::push_back_child(const node_ptr &parent, const node_ptr &child) { + child->parent_ = parent; + child->previous_sibling_ = parent->last_child_->previous_sibling_; + child->next_sibling_ = parent->last_child_; + parent->last_child_->previous_sibling_->next_sibling_ = child; + parent->last_child_->previous_sibling_ = child; + // set depth + // child->depth = depth + 1; +} + +bool schema::has_node(const std::type_index &index, const std::string &name) const { + return node_map_.count(name) > 0 || type_index_node_map_.count(index) > 0; +} + +} // namespace matador::object diff --git a/source/core/object/schema_node.cpp b/source/core/object/schema_node.cpp new file mode 100644 index 0000000..d6257cc --- /dev/null +++ b/source/core/object/schema_node.cpp @@ -0,0 +1,60 @@ +#include + +#include "matador/object/schema_node.hpp" + +namespace matador::object { + +schema_node::schema_node(schema &tree) +: schema_(tree) +, info_(std::make_unique(*this)) +{} + +std::string schema_node::name() const { + return name_; +} + +std::type_index schema_node::type_index() const { + return info_->type_index(); +} + +void schema_node::append(const std::shared_ptr &sibling) { + sibling->parent_ = parent_; + + // sibling->previous_sibling_ = this; + // sibling->next_sibling_ = next_sibling_; + // next_sibling_ = sibling; + // sibling->next_sibling_->previous_sibling_ = sibling; + // sibling->depth = depth; + + // if (!parent_) { + // sibling->op_first = new object_proxy(); + // sibling->op_last = sibling->op_marker = new object_proxy(); + // sibling->op_first->link(sibling->op_last); + // } else { + // throw object_exception("failed to add node as sibling: node has no parent"); + // } +} + +void schema_node::insert(const std::shared_ptr &child) { + // child->parent_ = this; + // child->previous_sibling_ = last_child_->previous_sibling_; + // child->next_sibling_ = last_child_; + // last_child_->previous_sibling_->next_sibling_ = child; + // last_child_->previous_sibling_ = child; + // set depth + // child->depth = depth + 1; + // set object proxy pointer + // 1. first + // if (op_first->next() == op_last) { + // // node hasn't any serializable (proxy) + // child->op_first = op_first; + // } else { + // // node has some objects (proxy) + // child->op_first = op_last->prev(); + // } + // // 2. marker + // child->op_marker = op_last; + // // 3. last + // child->op_last = op_last; +} +} diff --git a/source/core/utils/default_type_traits.cpp b/source/core/utils/default_type_traits.cpp new file mode 100644 index 0000000..b0aba04 --- /dev/null +++ b/source/core/utils/default_type_traits.cpp @@ -0,0 +1,184 @@ +#include "matador/utils/default_type_traits.hpp" + +#include "matador/utils/attribute_reader.hpp" +#include "matador/utils/attribute_writer.hpp" + +namespace matador::utils { + +void data_type_traits::read_value(attribute_reader &/*reader*/, const char * /*id*/, size_t /*index*/, nullptr_t &/*value*/) +{} + +void data_type_traits::bind_value(attribute_writer &/*binder*/, size_t /*index*/, nullptr_t &/*value*/) +{} + +void data_type_traits::read_value(attribute_reader &reader, const char *id, const size_t index, int8_t &value) +{ + reader.read_value(id, index, value); +} + +void data_type_traits::bind_value(attribute_writer &binder, const size_t index, const int8_t &value) +{ + binder.write_value(index, value); +} + +void data_type_traits::read_value(attribute_reader &reader, const char *id, const size_t index, int16_t &value) +{ + reader.read_value(id, index, value); +} + +void data_type_traits::bind_value(attribute_writer &binder, const size_t index, const int16_t &value) +{ + binder.write_value(index, value); +} + +void data_type_traits::read_value(attribute_reader &reader, const char *id, const size_t index, int32_t &value) +{ + reader.read_value(id, index, value); +} + +void data_type_traits::bind_value(attribute_writer &binder, const size_t index, const int32_t &value) +{ + binder.write_value(index, value); +} + +void data_type_traits::read_value(attribute_reader &reader, const char *id, const size_t index, int64_t &value) +{ + reader.read_value(id, index, value); +} + +void data_type_traits::bind_value(attribute_writer &binder, const size_t index, const int64_t &value) +{ + binder.write_value(index, value); +} + +void data_type_traits::read_value(attribute_reader &reader, const char *id, const size_t index, uint8_t &value) +{ + reader.read_value(id, index, value); +} + +void data_type_traits::bind_value(attribute_writer &binder, const size_t index, const uint8_t &value) +{ + binder.write_value(index, value); +} + +void data_type_traits::read_value(attribute_reader &reader, const char *id, const size_t index, uint16_t &value) +{ + reader.read_value(id, index, value); +} + +void data_type_traits::bind_value(attribute_writer &binder, const size_t index, const uint16_t &value) +{ + binder.write_value(index, value); +} + +void data_type_traits::read_value(attribute_reader &reader, const char *id, const size_t index, uint32_t &value) +{ + reader.read_value(id, index, value); +} + +void data_type_traits::bind_value(attribute_writer &binder, const size_t index, const uint32_t &value) +{ + binder.write_value(index, value); +} + +void data_type_traits::read_value(attribute_reader &reader, const char *id, const size_t index, uint64_t &value) +{ + reader.read_value(id, index, value); +} + +void data_type_traits::bind_value(attribute_writer &binder, const size_t index, const uint64_t &value) +{ + binder.write_value(index, value); +} + +void data_type_traits::read_value(attribute_reader &reader, const char *id, const size_t index, bool &value) +{ + reader.read_value(id, index, value); +} + +void data_type_traits::bind_value(attribute_writer &binder, const size_t index, const bool &value) +{ + binder.write_value(index, value); +} + +void data_type_traits::read_value(attribute_reader &reader, const char *id, const size_t index, float &value) +{ + reader.read_value(id, index, value); +} + +void data_type_traits::bind_value(attribute_writer &binder, const size_t index, const float &value) +{ + binder.write_value(index, value); +} + +void data_type_traits::read_value(attribute_reader &reader, const char *id, const size_t index, double &value) +{ + reader.read_value(id, index, value); +} + +void data_type_traits::bind_value(attribute_writer &binder, const size_t index, const double &value) +{ + binder.write_value(index, value); +} + +void data_type_traits::read_value(attribute_reader &reader, const char *id, const size_t index, const char* value, const size_t size) +{ + reader.read_value(id, index, const_cast(value), size); +} + +void data_type_traits::bind_value(attribute_writer &binder, const size_t index, const char *value, const size_t size) +{ + binder.write_value(index, value, size); +} + +void data_type_traits::read_value(attribute_reader &reader, const char *id, const size_t index, char* value, const size_t size) +{ + reader.read_value(id, index, value, size); +} + +void data_type_traits::bind_value(attribute_writer &binder, const size_t index, const char *value, const size_t size) +{ + binder.write_value(index, value, size); +} + +void data_type_traits::read_value(attribute_reader &reader, const char *id, size_t index, std::string &value, size_t size) +{ + reader.read_value(id, index, value, size); +} + +void data_type_traits::bind_value(attribute_writer &binder, size_t index, std::string &value, size_t size) +{ + binder.write_value(index, value, size); +} + +void data_type_traits::read_value(attribute_reader &reader, const char *id, size_t index, utils::blob &value) +{ + reader.read_value(id, index, value); +} + +void data_type_traits::bind_value(attribute_writer &binder, size_t index, utils::blob &value) +{ + binder.write_value(index, value); +} + +// void data_type_traits::read_value(attribute_reader &reader, const char *id, size_t index, date &value) +// { +// reader.read_value(id, index, value); +// } +// +// void data_type_traits::bind_value(attribute_writer &binder, size_t index, date &value) +// { +// binder.write_value(index, value); +// } +// +// void data_type_traits::read_value(attribute_reader &reader, const char *id, size_t index, time &value) +// { +// reader.read_value(id, index, value); +// } +// +// void data_type_traits::bind_value(attribute_writer &binder, size_t index, time &value) +// { +// binder.write_value(index, value); +// } + +} \ No newline at end of file diff --git a/source/core/utils/error.cpp b/source/core/utils/error.cpp new file mode 100644 index 0000000..8dfbddf --- /dev/null +++ b/source/core/utils/error.cpp @@ -0,0 +1,89 @@ +#include "matador/utils/error.hpp" + +#include +#include + +namespace matador::utils { + +std::string error::message() const { + return ec_.message(); +} + +std::string error::category() const { + return ec_.category().name(); +} + +std::error_code error::ec() const { + return ec_; +} + +const std::vector & error::error_trace() const { + return error_trace_; +} + +std::optional error::error_info(const std::string &key) const { + const auto info = error_infos_.find(key); + if (info == error_infos_.end()) { + return std::nullopt; + } + return info->second; +} + +void error::add_error_info(const std::string &key, std::string value) { + error_infos_[key] = std::move(value); +} + +void error::remove_error_info(const std::string &key) { + error_infos_.erase(key); +} + +std::vector error::error_info_keys() const { + std::vector keys; + + std::transform( + std::begin(error_infos_), + std::end(error_infos_), + std::back_inserter(keys), + [](const auto &pair){return pair.first;}); + + return keys; +} + +bool operator==(const error &lhs, const error &rhs) { + return lhs.ec_ == rhs.ec_; +} + +bool operator==(const error &lhs, const std::error_code &rhs) { + return lhs.ec_ == rhs; +} + +bool operator==(const error &lhs, const std::error_condition &rhs) { + return lhs.ec_ == rhs; +} + +bool operator!=(const error &lhs, const error &rhs) { + return !(lhs == rhs); +} + +bool operator!=(const error &lhs, const std::error_code &rhs) { + return !(lhs == rhs); +} + +bool operator!=(const error &lhs, const std::error_condition &rhs) { + return !(lhs == rhs); +} + +bool operator<(const error &lhs, const error &rhs) { + return lhs.ec_ < rhs.ec_; +} + +bool operator<(const error &lhs, const std::error_code &rhs) { + return lhs.ec_ < rhs; +} + +// std::ostream & operator<<(std::ostream &out, const error &err) { + // out << err.ec_ << " " << err.ec_.message(); + // return out; +// } + +} diff --git a/source/core/utils/errors.cpp b/source/core/utils/errors.cpp new file mode 100644 index 0000000..e1e6236 --- /dev/null +++ b/source/core/utils/errors.cpp @@ -0,0 +1,31 @@ +#include "matador/utils/errors.hpp" + +namespace matador::utils { + +const char * utils_category_impl::name() const noexcept { + return "utils"; +} + +std::string utils_category_impl::message(const int ev) const { + switch (static_cast(ev)) { + case (utils_error::InvalidVersionString): + return "Invalid version string"; + default: + return "Unknown error"; + } +} + +const std::error_category & sql_category() { + static utils_category_impl instance; + return instance; +} + +std::error_code make_error_code(utils_error e) { + return {static_cast(e), sql_category()}; +} + +std::error_condition make_error_condition(utils_error e) { + return {static_cast(e), sql_category()}; +} + +} diff --git a/source/core/utils/field_attributes.cpp b/source/core/utils/field_attributes.cpp new file mode 100644 index 0000000..ac64775 --- /dev/null +++ b/source/core/utils/field_attributes.cpp @@ -0,0 +1,38 @@ +#include "matador/utils/field_attributes.hpp" + +namespace matador::utils { + +field_attributes::field_attributes(const size_t size) + : size_(size) +{} + +field_attributes::field_attributes(const constraints options) + : options_(options) +{} + +field_attributes::field_attributes(const size_t size, const constraints options) + : size_(size), options_(options) +{} + +field_attributes& field_attributes::operator=(const size_t size) { + size_ = size; + options_ = constraints::NONE; + return *this; +} +field_attributes& field_attributes::operator=(constraints opt) { + options_ = opt; + size_ = 0; + return *this; +} + +size_t field_attributes::size() const +{ + return size_; +} + +constraints field_attributes::options() const +{ + return options_; +} + +} \ No newline at end of file diff --git a/src/utils/identifier.cpp b/source/core/utils/identifier.cpp similarity index 59% rename from src/utils/identifier.cpp rename to source/core/utils/identifier.cpp index 05051f6..21e72ec 100644 --- a/src/utils/identifier.cpp +++ b/source/core/utils/identifier.cpp @@ -1,27 +1,29 @@ #include "matador/utils/identifier.hpp" +#include #include #include namespace matador::utils { +size_t detail::hash(const char *value) { + return std::hash()(std::string_view(value, std::strlen(value))); +} -identifier::base::base(const std::type_index &ti, detail::identifier_type_t id_type) +bool identifier_type_traits::is_valid(const char *value) { + return value != nullptr && strlen(value) > 0; +} + +std::string identifier_type_traits::to_string(const char *value) { + return value; +} + +identifier::base::base(const std::type_index &ti, const basic_type type) : type_index_(ti) - , identifier_type_(id_type) + , type_(type) {} -bool identifier::base::is_similar_type(const identifier::base &x) const -{ - return identifier_type_ == x.type(); -} - -detail::identifier_type_t identifier::base::type() const -{ - return identifier_type_; -} - identifier::null_pk::null_pk() - : base(std::type_index(typeid(null_type_t)), detail::identifier_type_traits::type()) + : base(std::type_index(typeid(null_type_t)), basic_type::type_null) {} identifier::base* identifier::null_pk::copy() const @@ -29,24 +31,24 @@ identifier::base* identifier::null_pk::copy() const return new null_pk; } -bool identifier::null_pk::equal_to(const identifier::base &x) const +bool identifier::null_pk::equal_to(const base &x) const { return type_index_ == x.type_index_; } -bool identifier::null_pk::less(const identifier::base &x) const +bool identifier::null_pk::less(const base &x) const { return type_index_ == x.type_index_; } bool identifier::null_pk::is_valid() const { - return detail::identifier_type_traits::is_valid(); + return identifier_type_traits::is_valid(); } std::string identifier::null_pk::str() const { - return detail::identifier_type_traits::to_string(); + return identifier_type_traits::to_string(); } void identifier::null_pk::serialize(identifier_serializer &s) @@ -56,7 +58,7 @@ void identifier::null_pk::serialize(identifier_serializer &s) size_t identifier::null_pk::hash() const { - throw std::runtime_error("hash for null_pk not allowed"); + return std::hash()(nullptr); } identifier::identifier() @@ -74,10 +76,14 @@ identifier &identifier::operator=(const identifier &x) { } identifier::identifier(identifier &&x) noexcept - : id_(std::move(x.id_)) {} +: id_(std::move(x.id_)) { + x.clear(); +} identifier &identifier::operator=(identifier &&x) noexcept { id_ = std::move(x.id_); + x.clear(); + return *this; } @@ -105,11 +111,6 @@ bool identifier::operator>=(const identifier &x) const { return !operator<(x); } -bool identifier::is_similar_type(const identifier &x) const -{ - return id_->is_similar_type(*x.id_); -} - std::string identifier::str() const { return id_->str(); } @@ -118,6 +119,10 @@ const std::type_index &identifier::type_index() const { return id_->type_index_; } +basic_type identifier::type() const { + return id_->type_; +} + identifier identifier::share() const { return identifier(id_); @@ -128,6 +133,34 @@ size_t identifier::use_count() const return id_.use_count(); } +bool identifier::is_integer() const { + return id_->type_ >= basic_type::type_int8 && id_->type_ <= basic_type::type_uint64; +} + +bool identifier::is_floating_point() const { + return id_->type_ == basic_type::type_float || id_->type_ == basic_type::type_double; +} + +bool identifier::is_bool() const { + return id_->type_ == basic_type::type_bool; +} + +bool identifier::is_varchar() const { + return id_->type_ == basic_type::type_varchar; +} + +bool identifier::is_date() const { + return id_->type_ == basic_type::type_date; +} + +bool identifier::is_time() const { + return id_->type_ == basic_type::type_time; +} + +bool identifier::is_blob() const { + return id_->type_ == basic_type::type_blob; +} + bool identifier::is_null() const { return type_index() == null_identifier.type_index(); @@ -143,8 +176,7 @@ void identifier::clear() id_ = std::make_unique(); } -void identifier::serialize(identifier_serializer &s) -{ +void identifier::serialize(identifier_serializer &s) const { id_->serialize(s); } diff --git a/src/utils/library.cpp b/source/core/utils/library.cpp similarity index 61% rename from src/utils/library.cpp rename to source/core/utils/library.cpp index 5e5786c..a986e72 100644 --- a/src/utils/library.cpp +++ b/source/core/utils/library.cpp @@ -1,6 +1,8 @@ #include "matador/utils/library.hpp" #include "matador/utils/os.hpp" +#include + namespace matador::utils { library::library(std::string lib) @@ -14,27 +16,27 @@ library::~library() bool library::load() { - auto path = os::getenv("MATADOR_BACKENDS_PATH"); + auto path = os::getenv("MATADOR_BACKENDS_PATH"); #if defined(_MSC_VER) || defined(__MINGW32__) - auto cookie = AddDllDirectory(std::wstring(path.begin(), path.end()).c_str()); - handle_ = LoadLibraryExA((lib_ + ".dll").c_str(), nullptr, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); - RemoveDllDirectory(cookie); + auto cookie = AddDllDirectory(std::wstring(path.begin(), path.end()).c_str()); + handle_ = LoadLibraryExA((lib_ + ".dll").c_str(), nullptr, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); + RemoveDllDirectory(cookie); #elif defined(__APPLE__) - handle_ = dlopen(std::string("lib" + lib_ + ".dylib").c_str(), RTLD_LAZY); + handle_ = dlopen(std::string("lib" + lib_ + ".dylib").c_str(), RTLD_LAZY); #else handle_ = dlopen((path + "/lib" + lib_ + ".so").c_str(), RTLD_LAZY); #endif - if (!handle_) { + if (!handle_) { #if defined(_MSC_VER) || defined(__MINGW32__) - DWORD errorMessageID = ::GetLastError(); - auto errstr = utils::os::error_string(errorMessageID); + DWORD errorMessageID = ::GetLastError(); + auto errstr = utils::os::error_string(errorMessageID); #else - // TODO: handle win32 and linux error + // TODO: handle win32 and linux error fprintf(stdout, "dlopen error: %s", dlerror()); #endif - return false; - } - return true; + return false; + } + return true; } bool library::load(const std::string &lib) @@ -66,11 +68,10 @@ bool library::unload() func_ptr library::function(const std::string &f) const { #if defined(_MSC_VER) || defined(__MINGW32__) - auto *addr = GetProcAddress(handle_, f.c_str()); - return addr; + return GetProcAddress(handle_, f.c_str()); #else return dlsym(handle_, f.c_str()); #endif } -} \ No newline at end of file +} diff --git a/source/core/utils/os.cpp b/source/core/utils/os.cpp new file mode 100644 index 0000000..1e8e8fc --- /dev/null +++ b/source/core/utils/os.cpp @@ -0,0 +1,360 @@ +#include "matador/utils/os.hpp" + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#include +#else +#include +#endif + +namespace matador::utils::os { + +void setenv(const char *name, const char *value, override_env_value override_value) +{ +#ifdef _WIN32 + _putenv_s(name, value); +#else + ::setenv(name, value, static_cast(override_value)); +#endif +} + +#ifdef _WIN32 +std::string error_string(unsigned long error) { + char* lpMsgBuf; + auto bufLen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, + nullptr); + std::string result; + if (bufLen) { + result.append(lpMsgBuf, lpMsgBuf+bufLen); + LocalFree(lpMsgBuf); + } + return result; +} +#endif + +std::string getenv(const char *name) { +#ifdef _WIN32 + char var[1024]; + size_t len{}; + const auto error = getenv_s(&len, var, 1024, name); + if (error > 0) { + throw std::logic_error(error_string(error)); + }; + + return var; +#else + const char *path = ::getenv(name); + return path == nullptr ? "" : path; +#endif +} + +void unsetenv(const char *name) +{ +#ifdef _WIN32 + _putenv_s(name, nullptr); +#else + ::unsetenv(name); +#endif +} + +} + +namespace matador::os { +FILE* fopen(const std::string &path, const char *modes) +{ + return fopen(path.c_str(), modes); +} + +FILE* fopen(const char *path, const char *modes) +{ +#ifdef _WIN32 + return _fsopen(path, modes, _SH_DENYWR); +#else + return ::fopen(path, modes); +#endif +} + +FILE* freopen(const std::string &path, const char *modes, FILE *stream) +{ + return os::freopen(path.c_str(), modes, stream); +} + +FILE* freopen(const char *path, const char *modes, FILE *stream) +{ +#ifdef _WIN32 + FILE* redirected_stream; + freopen_s(&redirected_stream, path, "w+", stream); + return redirected_stream; +#else + return ::freopen(path, modes, stream); +#endif +} + +bool fclose(FILE *f) +{ + return ::fclose(f) == 0; +} + +bool remove(const std::string &name) +{ + return os::remove(name.c_str()); +} + +bool remove(const char *name) +{ + return ::remove(name) == 0; +} + +bool rename(const std::string &old_name, const std::string &new_name) +{ + return os::rename(old_name.c_str(), new_name.c_str()); +} + +bool rename(const char *old_name, const char *new_name) +{ + return ::rename(old_name, new_name) == 0; +} + +bool access(const std::string &path, int mode) +{ + return os::access(path.c_str(), mode); +} + +bool access(const char *path, int mode) +{ +#ifdef _WIN32 + return _access(path, mode) == 0; +#else + return ::access(path, mode) == 0; +#endif +} + +int dup(FILE *stream) +{ +#ifdef _WIN32 + return _dup(_fileno(stream)); +#else + return ::dup(fileno(stream)); +#endif +} + +bool mkdir(const std::string &dirname) +{ + return os::mkdir(dirname.c_str()); +} + +bool mkdir(const char *dirname) +{ + if (dirname == nullptr || strlen(dirname) == 0) { + return true; + } +#ifdef _WIN32 + return ::_mkdir(dirname) == 0; +#else + return ::mkdir(dirname, S_IRWXU) == 0; +#endif +} + +bool chdir(const std::string &dirname) +{ + return os::chdir(dirname.c_str()); +} + +bool chdir(const char *dirname) +{ + if (dirname == nullptr || strlen(dirname) == 0) { + return true; + } +#ifdef _WIN32 + return _chdir(dirname) == 0; +#else + return ::chdir(dirname) == 0; +#endif +} + +bool rmdir(const std::string &dirname) +{ + return os::rmdir(dirname.c_str()); +} + +bool rmdir(const char *dirname) +{ +#ifdef _WIN32 + return _rmdir(dirname) == 0; +#else + return ::rmdir(dirname) == 0; +#endif +} + +std::string get_current_dir() +{ + char buffer[1024]; +#ifdef _WIN32 + char *dir = _getcwd(buffer, 1024); +#else + char *dir = ::getcwd(buffer, 1024); +#endif + if (dir == nullptr) { +#ifdef _WIN32 + ::strerror_s(buffer, 1024, errno); + throw std::logic_error(buffer); +#else + throw std::logic_error(::strerror(errno)); +#endif + } + return std::string(buffer); +} + +bool mkpath(const std::string &path) +{ + return os::mkpath(path.c_str()); +} + +#ifdef _WIN32 +char DIR_SEPARATOR = '\\'; +const char* DIR_SEPARATOR_STRING = "\\"; +#else +char DIR_SEPARATOR = '/'; +const char* DIR_SEPARATOR_STRING = "/"; +#endif + +bool mkpath(const char *path) +{ + char tmp[256]; + char *p = nullptr; + size_t len; + + snprintf(tmp, sizeof(tmp),"%s",path); + len = strlen(tmp); + if (len == 0) { + return true; + } + if(tmp[len - 1] == DIR_SEPARATOR) { + tmp[len - 1] = 0; + } + for(p = tmp + 1; *p; p++) { + if (*p == DIR_SEPARATOR) { + *p = 0; + if (!os::mkdir(tmp)) { + return false; + } + *p = DIR_SEPARATOR; + } + } + return os::mkdir(tmp); +} + +bool rmpath(const std::string &path) +{ + return os::rmpath(path.c_str()); +} + +bool rmpath(const char *path) +{ + // change next to last path segment + + std::vector pathcopy(path, path+::strlen(path)+1); + std::vector segments; +#ifdef _WIN32 + char *next_token = nullptr; + char *segment = ::strtok_s(pathcopy.data(), DIR_SEPARATOR_STRING, &next_token); +#else + char *segment = ::strtok(pathcopy.data(), DIR_SEPARATOR_STRING); +#endif + + while (segment != nullptr) { + os::chdir(segment); + segments.emplace_back(segment); +#ifdef _WIN32 + segment = ::strtok_s(nullptr, DIR_SEPARATOR_STRING, &next_token); +#else + segment = ::strtok(nullptr, DIR_SEPARATOR_STRING); +#endif + } + + auto first = segments.rbegin(); + for (auto it=first; it!=segments.rend(); ++it) { + os::chdir(".."); + os::rmdir(*it); + } + return true; +} + +bool is_readable(const std::string &path) +{ + return is_readable(path.c_str()); +} + +bool is_readable(const char *path) +{ + return os::access(path, 2); +} + +bool is_writable(const std::string &path) +{ + return is_writable(path.c_str()); +} + +bool is_writable(const char *path) +{ + return os::access(path, 4); +} + +bool exists(const std::string &path) +{ + return exists(path.c_str()); +} + +bool exists(const char *path) +{ + return os::access(path, 0); +} + +size_t file_size(FILE *stream) +{ +#ifdef _WIN32 + int fd = _fileno(stream); + return ::_filelength(fd); +#else + int fd = ::fileno(stream); + struct stat buf{}; + ::fstat(fd, &buf); + return buf.st_size; +#endif +} + +std::string build_path(const std::string &a, const std::string &b) +{ + return a + DIR_SEPARATOR_STRING + b; +} + +char* strerror(int err, char* errbuf, size_t bufsize) +{ +#ifdef _WIN32 + ::strerror_s(errbuf, bufsize, err); + return errbuf; +#else + auto msg = ::strerror(err); + auto s = strlen(msg); + if (s < bufsize - 1) { + return strcpy(errbuf, msg); + } else { + errbuf[bufsize - 1] = '\0'; + return strncpy(errbuf, msg, bufsize - 1); + } +#endif +} + +} diff --git a/source/core/utils/string.cpp b/source/core/utils/string.cpp new file mode 100644 index 0000000..1bd247c --- /dev/null +++ b/source/core/utils/string.cpp @@ -0,0 +1,81 @@ +#include "matador/utils/string.hpp" +#include "matador/utils/export.hpp" + +#include + +namespace matador::utils { +std::string to_string(const blob& data) { + static constexpr char HEXITS[] = "0123456789ABCDEF"; + + std::string str(2 * data.size(), '\0'); + auto item = str.begin(); + + for(const auto c : data) { + *item++ = HEXITS[c >> 4]; + *item++ = HEXITS[c & 0x0F]; + } + + return str; +} + +size_t split(const std::string &str, char delim, std::vector &values) +{ + std::stringstream ss(str); + std::string item; + while (std::getline(ss, item, delim)) { + values.push_back(item); + } + return values.size(); +} + +std::vector split(const std::string &str, char delim) +{ + std::stringstream ss(str); + std::string item; + std::vector result; + while (std::getline(ss, item, delim)) { + result.push_back(item); + } + return result; +} + +size_t split(const std::string &str, const char delim, std::list &values) +{ + std::stringstream ss(str); + std::string item; + while (std::getline(ss, item, delim)) { + values.push_back(item); + } + return values.size(); +} + +#ifdef _MSC_VER +// const char* date_format::ISO8601 = "%Y-%m-%d"; +// const char* time_format::ISO8601 = "%Y-%m-%dT%H:%M:%S"; +#endif + +std::string trim(const std::string& str, const std::string& whitespace) +{ + const auto first = str.find_first_not_of(whitespace); + if (first == std::string::npos) + return ""; // no content + + const auto end = str.find_last_not_of(whitespace); + const auto range = end - first + 1; + + return str.substr(first, range); +} + +void replace_all(std::string &in, const std::string &from, const std::string &to) +{ + if(from.empty()) { + return; + } + size_t start_pos = 0; + while((start_pos = in.find(from, start_pos)) != std::string::npos) { + in.replace(start_pos, from.length(), to); + start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx' + } +} + +} diff --git a/source/core/utils/types.cpp b/source/core/utils/types.cpp new file mode 100644 index 0000000..f40f72c --- /dev/null +++ b/source/core/utils/types.cpp @@ -0,0 +1,61 @@ +#include "matador/utils/types.hpp" +#include "matador/utils/basic_types.hpp" + +namespace matador::utils { + +void initialize_by_basic_type(const basic_type type, database_type &val) +{ + switch (type) { + case basic_type::type_int8: + val.emplace(); + break; + case basic_type::type_int16: + val.emplace(); + break; + case basic_type::type_int32: + val.emplace(); + break; + case basic_type::type_int64: + val.emplace(); + break; + case basic_type::type_uint8: + val.emplace(); + break; + case basic_type::type_uint16: + val.emplace(); + break; + case basic_type::type_uint32: + val.emplace(); + break; + case basic_type::type_uint64: + val.emplace(); + break; + case basic_type::type_bool: + val.emplace(); + break; + case basic_type::type_float: + val.emplace(); + break; + case basic_type::type_double: + val.emplace(); + break; + case basic_type::type_varchar: + case basic_type::type_text: + val.emplace(); + break; + // case basic_type::type_date: + // val.emplace(); + // break; + // case basic_type::type_time: + // val.emplace
& t, std::string name, std::string as) +: table_(t) +, name(std::move(name)) +, alias(std::move(as)) { +} + +bool column::equals(const column &x) const +{ + return *table_ == *x.table_ && + name == x.name && + alias == x.alias && + function_ == x.function_; +} + +column &column::as(std::string a) +{ + alias = std::move(a); + return *this; +} + +bool column::is_function() const +{ + return function_ != sql_function_t::NONE; +} + +bool column::has_alias() const { + return !alias.empty(); +} + +} \ No newline at end of file diff --git a/source/orm/sql/column_definition.cpp b/source/orm/sql/column_definition.cpp new file mode 100644 index 0000000..ee58379 --- /dev/null +++ b/source/orm/sql/column_definition.cpp @@ -0,0 +1,176 @@ +#include "matador/sql/column_definition.hpp" + +#include +#include + +namespace matador::sql { + +column_definition::column_definition(const char *name) + : name_(name) + , attributes_(utils::null_attributes) +{} + +column_definition::column_definition(std::string name) + : name_(std::move(name)) + , attributes_(utils::null_attributes) +{} + +column_definition::column_definition(std::string name, const utils::basic_type type, const utils::field_attributes& attr, const null_option null_opt, const size_t index) + : name_(std::move(name)) + , index_(index) + , attributes_(attr) + , null_option_(null_opt) + , value_(type, attr.size()) +{} + +column_definition::column_definition(std::string name, + const utils::basic_type type, + const size_t index, + std::string ref_table, + std::string ref_column, + const utils::field_attributes& attr, const null_option null_opt) + : name_(std::move(name)) + , index_(index) + , attributes_(attr) + , null_option_(null_opt) + , value_(type, attr.size()) + , ref_table_(std::move(ref_table)) + , ref_column_(std::move(ref_column)) +{} + +const std::string &column_definition::name() const +{ + return name_; +} + +std::string column_definition::full_name() const +{ + return table_ + "." + name_; +} + +std::string column_definition::table_name() const +{ + return table_; +} + +int column_definition::index() const +{ + return index_; +} + +const utils::field_attributes &column_definition::attributes() const +{ + return attributes_; +} + +bool column_definition::is_nullable() const +{ + return null_option_ == null_option::NULLABLE; +} + +utils::basic_type column_definition::type() const +{ + return value_.type(); +} + +const std::string &column_definition::ref_table() const +{ + return ref_table_; +} + +const std::string &column_definition::ref_column() const +{ + return ref_column_; +} + +bool column_definition::is_foreign_reference() const +{ + return !ref_column_.empty() && !ref_table_.empty(); +} + +bool column_definition::is_integer() const +{ + return value_.is_integer(); +} + +bool column_definition::is_floating_point() const +{ + return value_.is_floating_point(); +} + +bool column_definition::is_bool() const +{ + return value_.is_bool(); +} + +bool column_definition::is_string() const +{ + return value_.is_string(); +} + +bool column_definition::is_varchar() const +{ + return value_.is_varchar(); +} + +bool column_definition::is_date() const +{ + return value_.is_date(); +} + +bool column_definition::is_time() const +{ + return value_.is_time(); +} + +bool column_definition::is_blob() const +{ + return value_.is_blob(); +} + +bool column_definition::is_null() const +{ + return value_.is_null(); +} + +void column_definition::type(utils::basic_type /*type*/) +{ + // type_ = type; +// utils::initialize_by_utils::basic_type(type, value_); +} + +std::string column_definition::str() const +{ + return value_.str(); +} + +std::ostream& operator<<(std::ostream &out, const column_definition &col) +{ + out << col.str(); + return out; +} + +column_definition make_column(const std::string &name, utils::basic_type type, utils::field_attributes attr, null_option null_opt) +{ + return {name, type, attr, null_opt}; +} + +template<> +column_definition make_column(const std::string &name, utils::field_attributes attr, null_option null_opt) +{ + return make_column(name, utils::data_type_traits::type(attr.size()), attr, null_opt); +} + +template<> +column_definition make_pk_column(const std::string &name, size_t size) +{ + return make_column(name, {size, utils::constraints::FOREIGN_KEY}); +} + +template<> +[[maybe_unused]] column_definition make_fk_column(const std::string &name, size_t size, const std::string &ref_table, + const std::string &ref_column) +{ + return {name, utils::data_type_traits::type(size), 0, ref_table, ref_column, {size, utils::constraints::FOREIGN_KEY}, null_option::NOT_NULL}; +} +} diff --git a/source/orm/sql/connection.cpp b/source/orm/sql/connection.cpp new file mode 100644 index 0000000..8df1a51 --- /dev/null +++ b/source/orm/sql/connection.cpp @@ -0,0 +1,228 @@ +#include "matador/sql/connection.hpp" + +#include "matador/sql/backend_provider.hpp" +#include "matador/sql/dialect.hpp" +#include "matador/sql/dialect_token.hpp" +// #include "matador/sql/schema.hpp" +// #include "matador/sql/query_compile_context.hpp" +#include "matador/sql/interface/connection_impl.hpp" + + +#include +#include +#include + +namespace matador::sql { + +connection::connection(connection_info info, const std::shared_ptr &sql_logger) +: connection_info_(std::move(info)) +, logger_(sql_logger) +{ + connection_.reset(backend_provider::instance().create_connection(connection_info_.type, connection_info_)); +} + +connection::connection(const std::string& dns, const std::shared_ptr &sql_logger) +: connection(connection_info::parse(dns), sql_logger) +{} + +connection::connection(const connection &x) +: connection_info_(x.connection_info_) +{ + if (x.connection_) { + throw std::runtime_error("couldn't copy connection with valid connection impl"); + } +} + +connection &connection::operator=(const connection &x) { + if (this == &x) { + return *this; + } + + connection_info_ = x.connection_info_; + if (x.connection_) { + throw std::runtime_error("couldn't copy connection with valid connection impl"); + } + return *this; +} + +connection & connection::operator=(connection &&x) noexcept { + connection_info_ = std::move(x.connection_info_); + connection_ = std::move(x.connection_); + logger_ = std::move(x.logger_); + + return *this; +} + +connection::~connection() +{ + if (connection_->is_open()) { + connection_->close(); + } + backend_provider::instance().destroy_connection(connection_info_.type, connection_.release()); + connection_ = nullptr; +} + +utils::result connection::open() const +{ + const auto res = is_open(); + if (res.is_error()) { + return utils::failure(res.err()); + } + if (*res) { + logger_->on_connect(); + return connection_->open(); + } + return utils::ok(); +} + +utils::result connection::close() const +{ + logger_->on_close(); + return connection_->close(); +} + +utils::result connection::is_open() const +{ + return connection_->is_open(); +} + +const connection_info &connection::info() const +{ + return connection_info_; +} + +std::string connection::type() const { + return connection_info_.type; +} + +utils::result connection::begin() const { + const auto res = connection_->execute(dialect().token_at(dialect_token::BEGIN)); + if (res.is_error()) { + return utils::failure(res.err()); + } + + return utils::ok(); +} + +utils::result connection::commit() const { + const auto res = connection_->execute(dialect().token_at(dialect_token::COMMIT)); + if (res.is_error()) { + return utils::failure(res.err()); + } + + return utils::ok(); +} + +utils::result connection::rollback() const { + const auto res = connection_->execute(dialect().token_at(dialect_token::ROLLBACK)); + if (res.is_error()) { + return utils::failure(res.err()); + } + + return utils::ok(); +} + +utils::result, utils::error> connection::describe(const std::string &table_name) const +{ + return connection_->describe(table_name); +} + +utils::result connection::exists(const std::string &schema_name, const std::string &table_name) const +{ + return connection_->exists(schema_name, table_name); +} + +utils::result connection::exists(const std::string &table_name) const +{ + return connection_->exists(dialect().default_schema_name(), table_name); +} + +utils::result connection::execute(const std::string &sql) const +{ +// logger_.debug(sql); + return connection_->execute(sql); +} + +bool has_unknown_columns(const std::vector &columns) { + return std::any_of(std::begin(columns), std::end(columns), [](const auto &col) { + return col.type() == utils::basic_type::type_null; + }); +} + +// query_result connection::fetch(const query_context &ctx) const +// { +// if (ctx.prototype.empty() || is_unknown(ctx.prototype)) { +// const auto table_prototype = describe(ctx.table.name); +// for (auto &col : ctx.prototype) { +// const auto rit = std::find_if(std::begin(table_prototype), std::end(table_prototype), [&col](const auto &value) { +// return value.name() == col.name(); +// }); +// if (col.type() == data_type::type_unknown && rit != table_prototype.end()) { +// const_cast(col).type(rit->type()); +// } +// } +// } +// // auto it = prototypes_.find(q.table_name); +// // if (it == prototypes_.end()) { +// // it = prototypes_.emplace(q.table_name, describe(q.table_name)).first; +// // } +// // // adjust columns from given query +// // for (auto &col : q.prototype) { +// // if (const auto rit = it->second.find(col.name()); col.type() == data_type_t::type_unknown && rit != it->second.end()) { +// // const_cast(col).type(rit->type()); +// // } +// // } +// auto res = fetch(ctx.sql); +// return query_result{std::move(res), ctx.prototype}; +// } + +utils::result, utils::error> connection::fetch(const query_context &ctx) const +{ +// logger_.debug(sql); + return connection_->fetch(ctx); + // return connection_->fetch(dialect().compile(ctx, *connection_)); +} + +utils::result connection::execute(const query_context& ctx) const +{ + return execute(ctx.sql); + // return execute(dialect().compile(ctx, *connection_).sql); +} + +utils::result connection::prepare(const query_context &ctx) const +{ + if (ctx.command != sql_command::SQL_CMD_CREATE && (ctx.prototype.empty() || has_unknown_columns(ctx.prototype))) { + if (const auto result = describe(ctx.table.name); result.is_ok()) { + for (auto &col: ctx.prototype) { + const auto rit = std::find_if(std::begin(*result), std::end(*result), + [&col](const auto &value) { + return value.name() == col.name(); + }); + if (col.type() == utils::basic_type::type_null && rit != result->end()) { + const_cast(col).type(rit->type()); + } + } + } + } + +// return connection_->prepare(qry).and_then([](auto &&res) { +// return statement(std::forward(res)); +// }); + if (auto result = connection_->prepare(ctx); result.is_ok()) { + return utils::ok(statement(result.release())); + } + + return utils::ok(statement(std::unique_ptr{})); +} + +std::string connection::str( const query_context& ctx ) const +{ + return ctx.sql; +} + +const class dialect &connection::dialect() const +{ + return connection_->dialect(); +} + +} diff --git a/src/sql/connection_info.cpp b/source/orm/sql/connection_info.cpp similarity index 100% rename from src/sql/connection_info.cpp rename to source/orm/sql/connection_info.cpp diff --git a/source/orm/sql/dialect.cpp b/source/orm/sql/dialect.cpp new file mode 100644 index 0000000..094bc26 --- /dev/null +++ b/source/orm/sql/dialect.cpp @@ -0,0 +1,136 @@ +#include "matador/sql/dialect.hpp" + +#include "matador/sql/interface/connection_impl.hpp" + +#include "matador/utils/string.hpp" + +namespace matador::sql { + +const std::string& dialect::token_at(const dialect_token token) const +{ + return tokens_.at(token); +} + +const std::string &dialect::data_type_at(const utils::basic_type type) const +{ + return data_types_.at(type); +} + +std::string dialect::prepare_identifier(const column &col) const +{ + std::string result; + if (!col.is_function()) { + if (!col.table_->name.empty()) { + result = prepare_identifier_string(col.table_->has_alias() ? col.table_->alias : col.table_->name) + "."; + } + result += prepare_identifier_string(col.name); + } else { + result = sql_func_map_.at(col.function_) + "(" + col.name + ")"; + } + if (!col.alias.empty()) { + result += " AS " + col.alias; + } + return result; +} + +std::string dialect::prepare_condition(const column& col) const +{ + std::string result; + if (!col.is_function()) { + // if (!col.alias.empty()) { + // result = col.alias; + // } else { + if (!col.table_->name.empty()) { + result = prepare_identifier_string(col.table_->has_alias() ? col.table_->alias : col.table_->name) + "."; + } + result += prepare_identifier_string(col.name); + // } + } else { + result = sql_func_map_.at(col.function_) + "(" + col.name + ")"; + } + + return result; +} + +const std::string& dialect::to_string(const bool val) const +{ + return bool_strings_[static_cast(val)]; +} + +void dialect::bool_strings(const std::string &true_string, const std::string &false_string) +{ + bool_strings_[0] = false_string; + bool_strings_[1] = true_string; +} + +std::string dialect::prepare_identifier_string(const std::string &col) const +{ + auto parts = utils::split(col, '.'); + + for (auto &part : parts) { + escape_quotes_in_identifier(part); + quote_identifier(part); + } + return utils::join(parts, "."); +} + +std::string dialect::prepare_literal(const std::string &str) const +{ + std::string result(str); + escape_quotes_in_literals(result); + return result; +} + +void dialect::quote_identifier(std::string &str) const +{ + str.insert(0, token_at(dialect_token::START_QUOTE)); + str += token_at(dialect_token::END_QUOTE); +} + +void dialect::escape_quotes_in_identifier(std::string &str) const +{ + const std::string open_char(token_at(dialect_token::START_QUOTE)); + const std::string close_char(token_at(dialect_token::END_QUOTE)); + if (identifier_escape_type() == escape_identifier_t::ESCAPE_CLOSING_BRACKET) { + utils::replace_all(str, close_char, close_char + close_char); + } else { + utils::replace_all(str, open_char, open_char + open_char); + } +} + +void dialect::escape_quotes_in_literals(std::string &str) const +{ + const std::string single_quote(token_at(dialect_token::STRING_QUOTE)); + const std::string double_quote(token_at(dialect_token::STRING_QUOTE) + token_at(dialect_token::STRING_QUOTE)); + utils::replace_all(str, single_quote, double_quote); +} + +dialect::escape_identifier_t dialect::identifier_escape_type() const { + return identifier_escape_type_; +} + +void dialect::identifier_escape_type(escape_identifier_t escape_identifier) { + identifier_escape_type_ = escape_identifier; +} + +std::string dialect::next_placeholder(const std::vector &bind_vars) const +{ + return placeholder_func_(bind_vars.size()); +} + +std::string dialect::to_escaped_string(const utils::blob &value, const connection_impl *conn) const +{ + if (conn != nullptr) { + return conn->to_escaped_string(value); + } + + return {}; + // return to_escaped_string_func_(value); +} + +std::string dialect::default_schema_name() const +{ + return default_schema_name_; +} + +} diff --git a/src/sql/dialect_builder.cpp b/source/orm/sql/dialect_builder.cpp similarity index 83% rename from src/sql/dialect_builder.cpp rename to source/orm/sql/dialect_builder.cpp index ea2197b..999e7a7 100644 --- a/src/sql/dialect_builder.cpp +++ b/source/orm/sql/dialect_builder.cpp @@ -45,8 +45,15 @@ dialect_builder &dialect_builder::with_default_schema_name(const std::string &sc return *this; } +dialect_builder &dialect_builder::with_bool_strings(const std::string &true_string, const std::string &false_string) +{ + dialect_.bool_strings(true_string, false_string); + + return *this; +} + dialect dialect_builder::build() { - return dialect_; + return std::move(dialect_); } } \ No newline at end of file diff --git a/source/orm/sql/error_code.cpp b/source/orm/sql/error_code.cpp new file mode 100644 index 0000000..2884256 --- /dev/null +++ b/source/orm/sql/error_code.cpp @@ -0,0 +1,59 @@ +#include "matador/sql/error_code.hpp" + +namespace matador::sql { + +const char * sql_category_impl::name() const noexcept { + return "sql"; +} + +std::string sql_category_impl::message(const int ev) const { + switch (static_cast(ev)) { + case error_code::OK: + return "OK"; + case error_code::INVALID_QUERY: + return "Invalid query"; + case error_code::UNKNOWN_TABLE: + return "Unknown table"; + case error_code::UNKNOWN_COLUMN: + return "Unknown column"; + case error_code::BIND_FAILED: + return "Bind failed"; + case error_code::EXECUTE_FAILED: + return "Execute failed"; + case error_code::FETCH_FAILED: + return "Fetch failed"; + case error_code::PREPARE_FAILED: + return "Prepare failed"; + case error_code::DESCRIBE_FAILED: + return "Describe failed"; + case error_code::TABLE_EXISTS_FAILED: + return "Table exists failed"; + case error_code::RETRIEVE_DATA_FAILED: + return "Retrieve data failed"; + case error_code::RESET_FAILED: + return "Reset failed"; + case error_code::OPEN_ERROR: + return "Open failed"; + case error_code::CLOSE_ERROR: + return "Close failed"; + case error_code::FAILURE: + return "Failure"; + default: + return "Unknown error"; + } +} + +const std::error_category & sql_category() { + static sql_category_impl instance; + return instance; +} + +std::error_code make_error_code(error_code e) { + return {static_cast(e), sql_category()}; +} + +std::error_condition make_error_condition(error_code e) { + return {static_cast(e), sql_category()}; +} + +} \ No newline at end of file diff --git a/source/orm/sql/executor.cpp b/source/orm/sql/executor.cpp new file mode 100644 index 0000000..8e5dab6 --- /dev/null +++ b/source/orm/sql/executor.cpp @@ -0,0 +1,5 @@ +#include "matador/sql/executor.hpp" + +namespace matador::sql { +executor::~executor() = default; +} diff --git a/src/sql/field.cpp b/source/orm/sql/field.cpp similarity index 86% rename from src/sql/field.cpp rename to source/orm/sql/field.cpp index c3364a0..6b6b5a6 100644 --- a/src/sql/field.cpp +++ b/source/orm/sql/field.cpp @@ -9,10 +9,10 @@ field::field(std::string name) , value_(nullptr) {} -field::field(std::string name, data_type_t data_type, size_t size, int index) +field::field(std::string name, const utils::basic_type dt, const size_t size, const int index) : name_(std::move(name)) , index_(index) - , value_(data_type, size) {} + , value_(dt, size) {} field::field(field &&x) noexcept : name_(std::move(x.name_)) @@ -57,7 +57,7 @@ std::ostream &operator<<(std::ostream &out, const field &col) std::string field::str() const { - return as().value(); + return as().value_or(""); } bool field::is_integer() const @@ -95,9 +95,4 @@ bool field::is_null() const return value_.is_null(); } -bool field::is_unknown() const -{ - return value_.is_unknown(); -} - } \ No newline at end of file diff --git a/source/orm/sql/interface/connection_impl.cpp b/source/orm/sql/interface/connection_impl.cpp new file mode 100644 index 0000000..3580d44 --- /dev/null +++ b/source/orm/sql/interface/connection_impl.cpp @@ -0,0 +1,20 @@ +#include "matador/sql/interface/connection_impl.hpp" + +#include "matador/sql/backend_provider.hpp" + +namespace matador::sql { + +connection_impl::connection_impl(const connection_info &info) +: info_(info) +, dialect_(backend_provider::instance().connection_dialect(info.type)) +{} + +const connection_info &connection_impl::info() const { + return info_; +} + +const class dialect & connection_impl::dialect() const { + return dialect_; +} + +} diff --git a/source/orm/sql/interface/query_result_reader.cpp b/source/orm/sql/interface/query_result_reader.cpp new file mode 100644 index 0000000..cf457b0 --- /dev/null +++ b/source/orm/sql/interface/query_result_reader.cpp @@ -0,0 +1,38 @@ +#include "matador/sql/interface/query_result_reader.hpp" + +#include "matador/utils/basic_types.hpp" +#include "matador/utils/convert.hpp" +#include "matador/utils/string.hpp" +#include "matador/utils/value.hpp" + +namespace matador::sql { + +template < typename Type > +void convert(const char *val_str, utils::value &val) +{ + if (val_str == nullptr) { + val = utils::value(Type{}); + return; + } + Type local_val{}; + const auto res = utils::to(val_str); + val = *res; +} + +// utils::blob query_result_reader::read_blob(const size_t index) +// { +// const auto *data = column(index); +// if (data == nullptr) { +// return {}; +// } +// const auto len = strlen(data); +// const auto *bytes = reinterpret_cast(data); +// return utils::blob{bytes, bytes+len}; +// } + +// utils::attribute_reader &query_result_reader::result_binder() +// { + // return empty_result_binder_; +// } + +} \ No newline at end of file diff --git a/source/orm/sql/interface/statement_impl.cpp b/source/orm/sql/interface/statement_impl.cpp new file mode 100644 index 0000000..6319144 --- /dev/null +++ b/source/orm/sql/interface/statement_impl.cpp @@ -0,0 +1,41 @@ +#include "matador/sql/interface/statement_impl.hpp" + +namespace matador::sql { + +statement_impl::statement_impl(query_context query) +: query_(std::move(query)) +{} + +void statement_impl::bind(const size_t pos, const char *value, const size_t size) +{ + utils::data_type_traits::bind_value(binder(), adjust_index(pos), value, size); +} + +void statement_impl::bind(const size_t pos, std::string &val, const size_t size) +{ + utils::data_type_traits::bind_value(binder(), adjust_index(pos), val, size); +} + +const std::vector &statement_impl::bind_vars() const +{ + return query_.bind_vars; +} + +bool statement_impl::is_valid_host_var(const std::string &host_var, const size_t pos) const +{ + const auto host_var_at_pos = bind_vars().at(pos); + + return host_var_at_pos == host_var; +} + +size_t statement_impl::start_index() const +{ + return 0; +} + +size_t statement_impl::adjust_index( size_t index ) const +{ + return index; +} + +} \ No newline at end of file diff --git a/source/orm/sql/internal/object_result_binder.cpp b/source/orm/sql/internal/object_result_binder.cpp new file mode 100644 index 0000000..01959cb --- /dev/null +++ b/source/orm/sql/internal/object_result_binder.cpp @@ -0,0 +1,46 @@ +#include "matador/sql/internal/object_result_binder.hpp" + +#include "matador/utils/data_type_traits.hpp" + +namespace matador::sql { + +namespace detail { + +void fk_result_binder::on_primary_key(const char * /*id*/, std::string &value, const size_t size) +{ + utils::data_type_traits::read_value(*binder_, id_, index_++, value, size); +} + +} + +void object_result_binder::reset() +{ + index_ = 0; +} + +void object_result_binder::on_primary_key(const char *id, std::string &value, const size_t size) +{ + utils::data_type_traits::read_value(*binder_, id, index_++, value, size); +} + +void object_result_binder::on_revision(const char *id, uint64_t &value) +{ + utils::data_type_traits::read_value(*binder_, id, index_++, value); +} + +void object_result_binder::on_attribute(const char *id, char *value, const utils::field_attributes &attr) +{ + utils::data_type_traits::read_value(*binder_, id, index_++, value, attr.size()); +} + +void object_result_binder::on_attribute(const char *id, std::string &value, const utils::field_attributes &attr) +{ + utils::data_type_traits::read_value(*binder_, id, index_++, value, attr.size()); +} + +void object_result_binder::on_attribute(const char * /*id*/, utils::value &/*val*/, const utils::field_attributes &/*attr*/) +{ + // utils::data_type_traits::read_value(*binder_, id, index_++, val); +} + +} \ No newline at end of file diff --git a/source/orm/sql/object_parameter_binder.cpp b/source/orm/sql/object_parameter_binder.cpp new file mode 100644 index 0000000..0680ea5 --- /dev/null +++ b/source/orm/sql/object_parameter_binder.cpp @@ -0,0 +1,30 @@ +#include "matador/sql/object_parameter_binder.hpp" +#include "matador/sql/interface/parameter_binder.hpp" + +namespace matador::sql { + +namespace detail { + +void fk_binder::on_primary_key(const char * /*id*/, std::string &value, size_t /*size*/) +{ + utils::data_type_traits::bind_value(*binder_, index_++, value); +} + +} + +void object_parameter_binder::reset(const size_t start_index) +{ + index_ = start_index; +} + +void object_parameter_binder::on_primary_key(const char * /*id*/, std::string &val, const size_t size) +{ + utils::data_type_traits::bind_value(*binder_, index_++, val, size); +} + +void object_parameter_binder::on_revision(const char * /*id*/, uint64_t &rev) +{ + utils::data_type_traits::bind_value(*binder_, index_++, rev); +} + +} \ No newline at end of file diff --git a/src/sql/query_result.cpp b/source/orm/sql/query_result.cpp similarity index 82% rename from src/sql/query_result.cpp rename to source/orm/sql/query_result.cpp index 46bbf82..bbda829 100644 --- a/src/sql/query_result.cpp +++ b/source/orm/sql/query_result.cpp @@ -1,7 +1,8 @@ #include "matador/sql/query_result.hpp" - #include "matador/sql/record.hpp" +#include "matador/sql/internal/query_result_impl.hpp" + namespace matador::sql::detail { template<> @@ -14,4 +15,5 @@ record *create_prototype(const std::vector &prototype return result.release(); } -} \ No newline at end of file + +} // namespace matador::sql diff --git a/src/sql/record.cpp b/source/orm/sql/record.cpp similarity index 90% rename from src/sql/record.cpp rename to source/orm/sql/record.cpp index 674c475..25f7b29 100644 --- a/src/sql/record.cpp +++ b/source/orm/sql/record.cpp @@ -26,8 +26,9 @@ record::record(const record &x) : fields_by_name_(x.fields_by_name_) //, pk_index_(x.pk_index_) { - for (auto& col : fields_by_name_) { - fields_.push_back(std::ref(col.second.first)); + for (auto& col : x.fields_) { + auto &it = fields_by_name_.at(col.get().name()); + fields_.push_back(std::ref(it.first)); } } @@ -40,8 +41,9 @@ record &record::operator=(const record &x) fields_by_name_ = x.fields_by_name_; fields_.clear(); // pk_index_ = x.pk_index_; - for (auto& col : fields_by_name_) { - fields_.push_back(std::ref(col.second.first)); + for (auto& col : x.fields_) { + auto &it = fields_by_name_.at(col.get().name()); + fields_.push_back(std::ref(it.first)); } return *this; } @@ -158,9 +160,6 @@ void record::init() void record::add_to_map(field &col, size_t index) { fields_by_name_.emplace(col.name(), field_index_pair {std::ref(col), index}); -// if (utils::is_constraint_set(col.attributes().options(), utils::constraints::PRIMARY_KEY)) { -// pk_index_ = static_cast(index); -// } } } diff --git a/source/orm/sql/statement.cpp b/source/orm/sql/statement.cpp new file mode 100644 index 0000000..38b8ffd --- /dev/null +++ b/source/orm/sql/statement.cpp @@ -0,0 +1,73 @@ +#include "matador/sql/statement.hpp" +#include "matador/sql/record.hpp" +#include "matador/sql/field.hpp" + +#include + +namespace matador::sql { + +statement::statement(std::unique_ptr impl, const std::shared_ptr &logger) +: statement_(std::move(impl)) +, logger_(logger) +{} + +statement &statement::bind(const size_t pos, const char *value) +{ + statement_->bind(pos, value, 0); + return *this; +} + +statement &statement::bind(const size_t pos, std::string &val, const size_t size) +{ + statement_->bind(pos, val, size); + return *this; +} + +utils::result statement::execute() const +{ +// logger_.info(statement_->query_.sql); + return statement_->execute(); +} + +//bool is_unknown(const std::vector &columns) { +// return std::all_of(std::begin(columns), std::end(columns), [](const auto &col) { +// return col.is_unknown(); +// }); +//} + +utils::result, utils::error> statement::fetch() const +{ +// if (is_unknown(statement_->query_.prototype)) { +// +// } + + auto result = statement_->fetch(); + if (!result.is_ok()) { + return utils::failure(result.err()); + } +// logger_.info(statement_->query_.sql); + return utils::ok(query_result{std::move(*result)}); +} + +utils::result, utils::error> statement::fetch_one() const { + // logger_.info(statement_->query_.sql); + auto result = statement_->fetch(); + if (!result.is_ok()) { + return utils::failure(result.err()); + } + + query_result records(std::move(*result)); + auto first = records.begin(); + if (first == records.end()) { + return utils::ok(std::optional{std::nullopt}); + } + + return utils::ok(std::optional{*first.release()}); +} + +void statement::reset() const +{ + statement_->reset(); +} + +} \ No newline at end of file diff --git a/source/orm/sql/table.cpp b/source/orm/sql/table.cpp new file mode 100644 index 0000000..6e8312a --- /dev/null +++ b/source/orm/sql/table.cpp @@ -0,0 +1,44 @@ +#include "matador/sql/table.hpp" + +namespace matador::sql { + +table::table(const char* name) +: table(std::string(name)) +{} + +table::table(std::string name) +: name(std::move(name)) +{} + +table::table(const char *name, std::string as) +: name(name) +, alias(std::move(as)) +{} + +table::table(std::string name, std::string as) +: name(std::move(name)) +, alias(std::move(as)) +{} + +bool table::operator==( const table& x ) const { + return name == x.name; +} + +table & table::as(const std::string &a) { + alias = a; + return *this; +} + +table table::as(const std::string &a) const { + return { name, a, columns }; +} + +bool table::has_alias() const { + return !alias.empty(); +} + +table operator ""_tab(const char *name, size_t len) { + return {{name, len}}; +} + +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index f61ad39..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,134 +0,0 @@ -set(SQL_SOURCES - sql/dialect.cpp - sql/query_builder.cpp - sql/column_definition.cpp - sql/key_value_pair.cpp - sql/basic_condition.cpp - sql/connection.cpp - sql/query_intermediates.cpp - sql/record.cpp - sql/connection_info.cpp - sql/connection_impl.cpp - sql/session.cpp - sql/backend_provider.cpp - sql/query_result_impl.cpp - sql/column_definition_generator.cpp - sql/column_generator.cpp - sql/key_value_generator.cpp - sql/fk_value_extractor.cpp - sql/schema.cpp - sql/query_result.cpp - sql/query_result_reader.cpp - sql/statement_cache.cpp - sql/statement_impl.cpp - sql/dialect_builder.cpp - sql/object_parameter_binder.cpp - sql/placeholder_generator.cpp - sql/data_type_traits.cpp - sql/result_parameter_binder.cpp - sql/statement.cpp - sql/convert.cpp - sql/column.cpp - sql/query.cpp - sql/query_parts.cpp - sql/query_compiler.cpp - sql/noop_connection.cpp - sql/query_part.cpp - sql/any_type_to_string_visitor.cpp - sql/field.cpp - sql/table_definition.cpp - sql/value.cpp - sql/entity_query_builder.cpp -) - -set(SQL_HEADER - ../include/matador/sql/dialect.hpp - ../include/matador/sql/query_builder.hpp - ../include/matador/sql/column_definition.hpp - ../include/matador/sql/data_type_traits.hpp - ../include/matador/sql/key_value_pair.hpp - ../include/matador/sql/basic_condition.hpp - ../include/matador/sql/condition.hpp - ../include/matador/sql/connection.hpp - ../include/matador/sql/query_intermediates.hpp - ../include/matador/sql/record.hpp - ../include/matador/sql/query_result.hpp - ../include/matador/sql/connection_impl.hpp - ../include/matador/sql/connection_info.hpp - ../include/matador/sql/connection_pool.hpp - ../include/matador/sql/session.hpp - ../include/matador/sql/backend_provider.hpp - ../include/matador/sql/query_result_impl.hpp - ../include/matador/sql/column_definition_generator.hpp - ../include/matador/sql/column_generator.hpp - ../include/matador/sql/value_extractor.hpp - ../include/matador/sql/any_type.hpp - ../include/matador/sql/key_value_generator.hpp - ../include/matador/sql/entity.hpp - ../include/matador/sql/fk_value_extractor.hpp - ../include/matador/sql/schema.hpp - ../include/matador/sql/any_type_to_visitor.hpp - ../include/matador/sql/query_result_reader.hpp - ../include/matador/sql/to_value.hpp - ../include/matador/sql/statement_cache.hpp - ../include/matador/sql/statement.hpp - ../include/matador/sql/statement_impl.hpp - ../include/matador/sql/query_context.hpp - ../include/matador/sql/parameter_binder.hpp - ../include/matador/sql/dialect_builder.hpp - ../include/matador/sql/object_parameter_binder.hpp - ../include/matador/sql/placeholder_generator.hpp - ../include/matador/sql/result_parameter_binder.hpp - ../include/matador/sql/convert.hpp - ../include/matador/sql/query.hpp - ../include/matador/sql/query_parts.hpp - ../include/matador/sql/query_part_visitor.hpp - ../include/matador/sql/query_compiler.hpp - ../include/matador/sql/query_data.hpp - ../include/matador/sql/table.hpp - ../include/matador/sql/noop_connection.hpp - ../include/matador/sql/query_part.hpp - ../include/matador/sql/any_type_to_string_visitor.hpp - ../include/matador/sql/query_helper.hpp - ../include/matador/sql/field.hpp - ../include/matador/sql/entity_query_builder.hpp - ../include/matador/sql/table_definition.hpp - ../include/matador/sql/has_many_to_many_relation.hpp - ../include/matador/sql/value.hpp -) - -set(UTILS_HEADER - ../include/matador/utils/field_attributes.hpp - ../include/matador/utils/string.hpp - ../include/matador/utils/constraints.hpp - ../include/matador/utils/library.hpp - ../include/matador/utils/os.hpp - ../include/matador/utils/access.hpp - ../include/matador/utils/identifier.hpp - ../include/matador/utils/cascade_type.hpp - ../include/matador/utils/logger.hpp - ../include/matador/utils/enum_mapper.hpp - ../include/matador/utils/types.hpp - ../include/matador/utils/foreign_attributes.hpp - ../include/matador/utils/fetch_type.hpp - ../include/matador/utils/result.hpp -) - -set(UTILS_SOURCES - utils/field_attributes.cpp - utils/string.cpp - sql/condition.cpp - utils/library.cpp - utils/os.cpp - utils/identifier.cpp - sql/value_extractor.cpp - utils/logger.cpp - utils/foreign_attributes.cpp -) - -add_library(matador STATIC - ${SQL_SOURCES} ${SQL_HEADER} - ${UTILS_SOURCES} ${UTILS_HEADER} -) - -target_include_directories(matador PUBLIC ${PROJECT_SOURCE_DIR}/include) diff --git a/src/sql/any_type_to_string_visitor.cpp b/src/sql/any_type_to_string_visitor.cpp deleted file mode 100644 index 7112c8a..0000000 --- a/src/sql/any_type_to_string_visitor.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "matador/sql/any_type_to_string_visitor.hpp" - -#include "matador/sql/dialect.hpp" -#include "matador/sql/query_context.hpp" - -#include "matador/utils/string.hpp" - -namespace matador::sql { - -any_type_to_string_visitor::any_type_to_string_visitor(const dialect &d, query_context &query) -: d(d), query(query) -{} - -void any_type_to_string_visitor::to_string(const char *val) -{ - result = "'" + d.prepare_literal(val) + "'"; -} - -void any_type_to_string_visitor::to_string(std::string &val) -{ - result = "'" + d.prepare_literal(val) + "'"; -} - -void any_type_to_string_visitor::to_string(utils::blob &val) -{ - // "This is a binary Data string" as binary data: - // MySQL: X'5468697320697320612062616E617279204461746120737472696E67' - // Postgres: E'\\x5468697320697320612062616E617279204461746120737472696E67' - // MSSQL: 0x5468697320697320612062616E617279204461746120737472696E67 - // Sqlite: X'5468697320697320612062616E617279204461746120737472696E67' - result = d.token_at(dialect::token_t::BEGIN_BINARY_DATA) + utils::to_string(val) + d.token_at(dialect::token_t::END_BINARY_DATA); -} - -void any_type_to_string_visitor::to_string(placeholder &/*val*/) -{ - query.bind_vars.emplace_back("unknown"); - result = d.next_placeholder(query.bind_vars); -} - -} \ No newline at end of file diff --git a/src/sql/basic_condition.cpp b/src/sql/basic_condition.cpp deleted file mode 100644 index bd46990..0000000 --- a/src/sql/basic_condition.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "matador/sql/basic_condition.hpp" - -namespace matador::sql { - -std::unordered_map basic_condition::operands{ - {operand_t::EQUAL, "="}, - {operand_t::NOT_EQUAL, "<>"}, - {operand_t::LESS, "<"}, - {operand_t::LESS_EQUAL, "<="}, - {operand_t::GREATER, ">"}, - {operand_t::GREATER_EQUAL, ">="}, - {operand_t::OR, "OR"}, - {operand_t::AND, "AND"}, - {operand_t::NOT, "NOT"}, - {operand_t::IN_LIST, "IN"}, - {operand_t::LIKE, "LIKE"} -}; - -basic_column_condition::basic_column_condition(column fld, basic_condition::operand_t op) - : field_(std::move(fld)), operand(basic_condition::operands[op]) -{ } - -basic_in_condition::basic_in_condition(column fld) - : field_(std::move(fld)) -{ } -} \ No newline at end of file diff --git a/src/sql/column.cpp b/src/sql/column.cpp deleted file mode 100644 index 3c2c0c9..0000000 --- a/src/sql/column.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "matador/sql/column.hpp" -#include "matador/sql/table.hpp" - -namespace matador::sql { - -column operator ""_col(const char *name, size_t len) -{ - return {{name, len}}; -} - -column::column(const char *name) : name(name) {} - -column::column(std::string name) : name(std::move(name)) {} - -column::column(sql_function_t func, std::string name) : name(std::move(name)), function_(func) {} - -column::column(std::string table_name, std::string name, std::string as) -: table(std::move(table_name)) -, name(std::move(name)) -, alias(std::move(as)) {} - -column::column(std::string table_name, const char *name, std::string as) -: table(std::move(table_name)) -, name(name) -, alias(std::move(as)) {} - -column::column(struct table &t, const char *name, std::string as) -: table(t.name) -, name(name) -, alias(std::move(as)) -{ - t.columns.push_back(*this); -} - -bool column::equals(const column &x) const -{ - return table == x.table && - name == x.name && - alias == x.alias && - function_ == x.function_; -} - -column &column::as(std::string a) -{ - alias = std::move(a); - return *this; -} - -bool column::is_function() const -{ - return function_ != sql_function_t::NONE; -} -} \ No newline at end of file diff --git a/src/sql/column_definition.cpp b/src/sql/column_definition.cpp deleted file mode 100644 index 97ad391..0000000 --- a/src/sql/column_definition.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include "matador/sql/column_definition.hpp" - -#include -#include - -namespace matador::sql { - -column_definition::column_definition(const char *name) - : name_(name), attributes_(utils::null_attributes) -{} - -column_definition::column_definition(std::string name) - : name_(std::move(name)), attributes_(utils::null_attributes) -{} - -column_definition::column_definition(std::string name, data_type_t type, utils::field_attributes attr, null_option null_opt, size_t index) - : name_(std::move(name)), index_(index), type_(type), attributes_(attr), null_option_(null_opt) -{} - -column_definition::column_definition(std::string name, data_type_t type, size_t index, std::string ref_table, std::string ref_column, - utils::field_attributes attr, null_option null_opt) - : name_(std::move(name)) - , index_(index) - , type_(type) - , attributes_(attr) - , null_option_(null_opt) - , ref_table_(std::move(ref_table)) - , ref_column_(std::move(ref_column)) -{} - -const std::string &column_definition::name() const -{ - return name_; -} - -int column_definition::index() const -{ - return index_; -} - -const utils::field_attributes &column_definition::attributes() const -{ - return attributes_; -} - -bool column_definition::is_nullable() const -{ - return null_option_ == null_option::NULLABLE; -} - -data_type_t column_definition::type() const -{ - return type_; -} - -const std::string &column_definition::ref_table() const -{ - return ref_table_; -} - -const std::string &column_definition::ref_column() const -{ - return ref_column_; -} - -bool column_definition::is_integer() const -{ - return type_ >= data_type_t::type_char && type_ <= data_type_t::type_unsigned_long_long; -} - -bool column_definition::is_floating_point() const -{ - return type_ == data_type_t::type_float || type_ == data_type_t::type_double; -} - -bool column_definition::is_bool() const -{ - return type_ == data_type_t::type_bool; -} - -bool column_definition::is_string() const -{ - return type_ == data_type_t::type_text; -} - -bool column_definition::is_varchar() const -{ - return type_ == data_type_t::type_varchar; -} - -bool column_definition::is_blob() const -{ - return type_ == data_type_t::type_blob; -} - -bool column_definition::is_null() const -{ - return type_ == data_type_t::type_null; -} - -bool column_definition::is_unknown() const -{ - return type_ == data_type_t::type_unknown; -} - -void column_definition::type(data_type_t type) -{ - type_ = type; -} - -std::string column_definition::str() const -{ - if (std::holds_alternative(value_)) { - return std::get(value_); - } - - any_type_to_visitor visitor; - std::visit(visitor, const_cast(value_)); - return visitor.result; -} - -std::ostream& operator<<(std::ostream &out, const column_definition &col) -{ - out << col.str(); - return out; -} - -column_definition make_column(const std::string &name, data_type_t type, utils::field_attributes attr, null_option null_opt) -{ - return {name, type, attr, null_opt}; -} - -template<> -column_definition make_column(const std::string &name, utils::field_attributes attr, null_option null_opt) -{ - return make_column(name, data_type_traits::builtin_type(attr.size()), attr, null_opt); -} - -template<> -column_definition make_pk_column(const std::string &name, size_t size) -{ - return make_column(name, {size, utils::constraints::FOREIGN_KEY}); -} - -template<> -[[maybe_unused]] column_definition make_fk_column(const std::string &name, size_t size, const std::string &ref_table, - const std::string &ref_column) -{ - return {name, data_type_traits::builtin_type(size), 0, ref_table, ref_column, {size, utils::constraints::FOREIGN_KEY}, null_option::NOT_NULL}; -} -} diff --git a/src/sql/column_definition_generator.cpp b/src/sql/column_definition_generator.cpp deleted file mode 100644 index 1a7068a..0000000 --- a/src/sql/column_definition_generator.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "matador/sql/column_definition_generator.hpp" -#include "matador/sql/schema.hpp" - -namespace matador::sql { - -column_definition_generator::column_definition_generator(std::vector &columns, const schema &repo) -: columns_(columns) -, repo_(repo) -{} - -void column_definition_generator::on_primary_key(const char *id, std::string &pk, size_t size) -{ - on_attribute(id, pk, { size, utils::constraints::PRIMARY_KEY }); -} - -void column_definition_generator::on_revision(const char *id, unsigned long long int &x) -{ - on_attribute(id, x); -} - -std::pair column_definition_generator::determine_foreign_ref(const std::type_index &ti) -{ - return repo_.reference(ti); -} - -void fk_column_generator::on_primary_key(const char *, std::string &, size_t size) -{ - type_ = data_type_traits::builtin_type(size); -} - -} \ No newline at end of file diff --git a/src/sql/column_generator.cpp b/src/sql/column_generator.cpp deleted file mode 100644 index d321896..0000000 --- a/src/sql/column_generator.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "matador/sql/column_generator.hpp" - -namespace matador::sql { - -column_generator::column_generator(std::vector &column_infos, - const sql::schema &ts, - const std::string &table_name, - bool force_lazy) -: column_infos_(column_infos) -, table_schema_(ts) -, force_lazy_(force_lazy) -{ - table_name_stack_.push(table_name); -} - -void column_generator::on_primary_key(const char *id, std::string &, size_t) -{ - push(id); -} - -void column_generator::on_revision(const char *id, unsigned long long int &) -{ - push(id); -} - -void column_generator::push(const std::string &column_name) -{ - char str[4]; - snprintf(str, 4, "c%02d", ++column_index); - column_infos_.emplace_back(table_name_stack_.top(), column_name, str); -} - -} \ No newline at end of file diff --git a/src/sql/condition.cpp b/src/sql/condition.cpp deleted file mode 100644 index 92b19d0..0000000 --- a/src/sql/condition.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "matador/sql/condition.hpp" - -#include "matador/sql/query_intermediates.hpp" - -namespace matador::sql { - -condition::type>::condition(const column &fld, basic_condition::operand_t op, const placeholder &val) -: basic_column_condition(fld, op), value(val) -{} - -std::string condition::type>::evaluate(const dialect &d, query_context &query) const -{ - query.bind_vars.emplace_back(field_.name); - return d.prepare_identifier(field_) + " " + operand + " " + d.next_placeholder(query.bind_vars); -} - -condition::condition(column col, basic_condition::operand_t op, const query_context &q) -: basic_column_condition(std::move(col), op), query_(q) -{} - -std::string condition::evaluate(const dialect &d, query_context &query) const -{ - std::string result(d.prepare_identifier(field_) + " " + operand + " ("); - result += query_.sql + (")"); - return result; -} - -condition in( const column& col, const query_context &q ) { - return {col, basic_condition::operand_t::IN_LIST, q}; -} - -condition in( const column& col, const query_select& q ) { - return in(col, q.build()); -} - -condition like( const column& col, const std::string& val ) { - return {col, basic_condition::operand_t::LIKE, val}; -} - -condition equals( const column& col, query_context& q ) { - return {col, basic_condition::operand_t::EQUAL, q}; -} - -condition operator==(const column &a, const column &b) -{ - return {a, basic_condition::operand_t::EQUAL, b}; -} -} \ No newline at end of file diff --git a/src/sql/connection.cpp b/src/sql/connection.cpp deleted file mode 100644 index 77e4d6a..0000000 --- a/src/sql/connection.cpp +++ /dev/null @@ -1,148 +0,0 @@ -#include "matador/sql/connection.hpp" - -#include "matador/sql/backend_provider.hpp" -#include "matador/sql/connection_impl.hpp" -#include "matador/sql/schema.hpp" - -#include -#include -#include - -namespace matador::sql { - -connection::connection(connection_info info) -: connection_info_(std::move(info)) -, logger_(stdout, "SQL") -, dialect_(backend_provider::instance().connection_dialect(connection_info_.type)) -{ - connection_.reset(backend_provider::instance().create_connection(connection_info_.type, connection_info_)); -} - -connection::connection(const std::string& dns) -: connection(connection_info::parse(dns)) -{} - -connection::connection(const connection &x) -: connection_info_(x.connection_info_) -, logger_(x.logger_) -, dialect_(x.dialect_) -{ - if (x.connection_) { - throw std::runtime_error("couldn't copy connection with valid connection impl"); - } -} - -connection &connection::operator=(const connection &x) { - connection_info_ = x.connection_info_; - logger_ = x.logger_; - if (x.connection_) { - throw std::runtime_error("couldn't copy connection with valid connection impl"); - } - return *this; -} - -connection::~connection() -{ - if (connection_->is_open()) { - connection_->close(); - } - backend_provider::instance().destroy_connection(connection_info_.type, connection_.release()); - connection_ = nullptr; -} - -void connection::open() -{ - connection_->open(); -} - -void connection::close() -{ - connection_->close(); -} - -bool connection::is_open() const -{ - return connection_->is_open(); -} - -const connection_info &connection::info() const -{ - return connection_info_; -} - -std::vector connection::describe(const std::string &table_name) const -{ - return std::move(connection_->describe(table_name)); -} - -bool connection::exists(const std::string &schema_name, const std::string &table_name) const -{ - return connection_->exists(schema_name, table_name); -} - -bool connection::exists(const std::string &table_name) const -{ - return connection_->exists(dialect_.default_schema_name(), table_name); -} - -size_t connection::execute(const std::string &sql) const -{ - logger_.debug(sql); - return connection_->execute(sql); -} - -sql::query connection::query(const schema &schema) const -{ - return sql::query(*const_cast(this), schema); -} - -bool is_unknown(const std::vector &columns) { - return std::all_of(std::begin(columns), std::end(columns), [](const auto &col) { - return col.type() == data_type_t::type_unknown; - }); -} - -query_result connection::fetch(const query_context &q) const -{ - if (q.prototype.empty() || is_unknown(q.prototype)) { - const auto table_prototype = describe(q.table.name); - for (auto &col : q.prototype) { - const auto rit = std::find_if(std::begin(table_prototype), std::end(table_prototype), [&col](const auto &value) { - return value.name() == col.name(); - }); - if (col.type() == data_type_t::type_unknown && rit != table_prototype.end()) { - const_cast(col).type(rit->type()); - } - } - } -// auto it = prototypes_.find(q.table_name); -// if (it == prototypes_.end()) { -// it = prototypes_.emplace(q.table_name, describe(q.table_name)).first; -// } -// // adjust columns from given query -// for (auto &col : q.prototype) { -// if (const auto rit = it->second.find(col.name()); col.type() == data_type_t::type_unknown && rit != it->second.end()) { -// const_cast(col).type(rit->type()); -// } -// } - auto res = fetch(q.sql); - return query_result{std::move(res), q.prototype}; -} - -std::unique_ptr connection::fetch(const std::string &sql) const -{ - logger_.debug(sql); - return connection_->fetch(sql); -} - -statement connection::prepare(query_context &&query) const -{ - return statement(connection_->prepare(std::move(query)), logger_); -} - -const class dialect &connection::dialect() const -{ - return dialect_; -} - -} diff --git a/src/sql/connection_impl.cpp b/src/sql/connection_impl.cpp deleted file mode 100644 index 6850699..0000000 --- a/src/sql/connection_impl.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "matador/sql/connection_impl.hpp" - -namespace matador::sql { - -connection_impl::connection_impl(const connection_info &info) -: info_(info){} - -const connection_info &connection_impl::info() const { - return info_; -} - -} \ No newline at end of file diff --git a/src/sql/convert.cpp b/src/sql/convert.cpp deleted file mode 100644 index 8d748f1..0000000 --- a/src/sql/convert.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "matador/sql/convert.hpp" - -#include - -namespace matador::sql { - -void convert(std::string &dest, bool source) -{ - dest = source ? "true" : "false"; -} - -void convert(std::string &dest, const char *source) -{ - dest.assign(source); -} - -long long to_long_long(const char *source) -{ - if (strlen(source) == 0) { - return{}; - } - char *end; - const auto result = strtoll(source, &end, 10); - if (end == nullptr) { - // Todo: check error - throw std::logic_error("couldn't convert value to number"); - } - - return result; -} - -unsigned long long to_unsigned_long_long(const char *source) -{ - if (strlen(source) == 0) { - return{}; - } - char *end; - const auto result = strtoull(source, &end, 10); - if (end == nullptr) { - // Todo: check error - throw std::logic_error("couldn't convert value to number"); - } - - return result; -} - -long double to_double(const char *source) -{ - if (strlen(source) == 0) { - return{}; - } - char *end; - const auto result = strtold(source, &end); - if (end == nullptr) { - // Todo: check error - throw std::logic_error("couldn't convert value to number"); - } - - return result; -} - -void convert(utils::blob &dest, const utils::blob &data) -{ - dest = data; -} - -} \ No newline at end of file diff --git a/src/sql/data_type_traits.cpp b/src/sql/data_type_traits.cpp deleted file mode 100644 index 5fedc18..0000000 --- a/src/sql/data_type_traits.cpp +++ /dev/null @@ -1,278 +0,0 @@ -#include "matador/sql/data_type_traits.hpp" - -#include "matador/sql/parameter_binder.hpp" -#include "matador/sql/query_result_reader.hpp" -#include "matador/sql/result_parameter_binder.hpp" - -namespace matador::sql { - -void data_type_traits::read_value(query_result_reader &reader, const char *id, size_t index, nullptr_t &/*value*/) -{ -// reader.read_value(id, index, value); -} - -void data_type_traits::bind_value(parameter_binder &binder, size_t index, nullptr_t &/*value*/) -{ -// binder.bind(index, value); -} - -void data_type_traits::bind_result_value(result_parameter_binder &binder, size_t index, nullptr_t &/*value*/) -{ -// binder.bind_result_value(index, value); -} - -void data_type_traits::read_value(query_result_reader &reader, const char *id, size_t index, char &value) -{ - reader.read_value(id, index, value); -} - -void data_type_traits::bind_value(parameter_binder &binder, size_t index, char &value) -{ - binder.bind(index, value); -} - -void data_type_traits::bind_result_value(result_parameter_binder &binder, size_t index, char &value) -{ - binder.bind_result_value(index, value); -} - -void data_type_traits::read_value(query_result_reader &reader, const char *id, size_t index, short &value) -{ - reader.read_value(id, index, value); -} - -void data_type_traits::bind_value(parameter_binder &binder, size_t index, short &value) -{ - binder.bind(index, value); -} - -void data_type_traits::bind_result_value(result_parameter_binder &binder, size_t index, short &value) -{ - binder.bind_result_value(index, value); -} - -void data_type_traits::read_value(query_result_reader &reader, const char *id, size_t index, int &value) -{ - reader.read_value(id, index, value); -} - -void data_type_traits::bind_value(parameter_binder &binder, size_t index, int &value) -{ - binder.bind(index, value); -} - -void data_type_traits::bind_result_value(result_parameter_binder &binder, size_t index, int &value) -{ - binder.bind_result_value(index, value); -} - -void data_type_traits::read_value(query_result_reader &reader, const char *id, size_t index, long &value) -{ - reader.read_value(id, index, value); -} - -void data_type_traits::bind_value(parameter_binder &binder, size_t index, long &value) -{ - binder.bind(index, value); -} - -void data_type_traits::bind_result_value(result_parameter_binder &binder, size_t index, long &value) -{ - binder.bind_result_value(index, value); -} - -void data_type_traits::read_value(query_result_reader &reader, const char *id, size_t index, long long &value) -{ - reader.read_value(id, index, value); -} - -void data_type_traits::bind_value(parameter_binder &binder, size_t index, long long int &value) -{ - binder.bind(index, value); -} - -void data_type_traits::bind_result_value(result_parameter_binder &binder, size_t index, long long int &value) -{ - binder.bind_result_value(index, value); -} - -void data_type_traits::read_value(query_result_reader &reader, const char *id, size_t index, unsigned char &value) -{ - reader.read_value(id, index, value); -} - -void data_type_traits::bind_value(parameter_binder &binder, size_t index, unsigned char &value) -{ - binder.bind(index, value); -} - -void data_type_traits::bind_result_value(result_parameter_binder &binder, size_t index, unsigned char &value) -{ - binder.bind_result_value(index, value); -} - -void data_type_traits::read_value(query_result_reader &reader, const char *id, size_t index, unsigned short &value) -{ - reader.read_value(id, index, value); -} - -void data_type_traits::bind_value(parameter_binder &binder, size_t index, unsigned short &value) -{ - binder.bind(index, value); -} - -void data_type_traits::bind_result_value(result_parameter_binder &binder, size_t index, unsigned short &value) -{ - binder.bind_result_value(index, value); -} - -void data_type_traits::read_value(query_result_reader &reader, const char *id, size_t index, unsigned int &value) -{ - reader.read_value(id, index, value); -} - -void data_type_traits::bind_value(parameter_binder &binder, size_t index, unsigned int &value) -{ - binder.bind(index, value); -} - -void data_type_traits::bind_result_value(result_parameter_binder &binder, size_t index, unsigned int &value) -{ - binder.bind_result_value(index, value); -} - -void data_type_traits::read_value(query_result_reader &reader, const char *id, size_t index, unsigned long &value) -{ - reader.read_value(id, index, value); -} - -void data_type_traits::bind_value(parameter_binder &binder, size_t index, unsigned long &value) -{ - binder.bind(index, value); -} - -void data_type_traits::bind_result_value(result_parameter_binder &binder, size_t index, unsigned long &value) -{ - binder.bind_result_value(index, value); -} - -void data_type_traits::read_value(query_result_reader &reader, const char *id, size_t index, unsigned long long &value) -{ - reader.read_value(id, index, value); -} - -void data_type_traits::bind_value(parameter_binder &binder, size_t index, unsigned long long &value) -{ - binder.bind(index, value); -} - -void data_type_traits::bind_result_value(result_parameter_binder &binder, size_t index, unsigned long long &value) -{ - binder.bind_result_value(index, value); -} - -void data_type_traits::read_value(query_result_reader &reader, const char *id, size_t index, bool &value) -{ - reader.read_value(id, index, value); -} - -void data_type_traits::bind_value(parameter_binder &binder, size_t index, bool &value) -{ - binder.bind(index, value); -} - -void data_type_traits::bind_result_value(result_parameter_binder &binder, size_t index, bool &value) -{ - binder.bind_result_value(index, value); -} - -void data_type_traits::read_value(query_result_reader &reader, const char *id, size_t index, float &value) -{ - reader.read_value(id, index, value); -} - -void data_type_traits::bind_value(parameter_binder &binder, size_t index, float &value) -{ - binder.bind(index, value); -} - -void data_type_traits::bind_result_value(result_parameter_binder &binder, size_t index, float &value) -{ - binder.bind_result_value(index, value); -} - -void data_type_traits::read_value(query_result_reader &reader, const char *id, size_t index, double &value) -{ - reader.read_value(id, index, value); -} - -void data_type_traits::bind_value(parameter_binder &binder, size_t index, double &value) -{ - binder.bind(index, value); -} - -void data_type_traits::bind_result_value(result_parameter_binder &binder, size_t index, double &value) -{ - binder.bind_result_value(index, value); -} - -void data_type_traits::read_value(query_result_reader &reader, const char *id, size_t index, const char* value, size_t size) -{ - reader.read_value(id, index, const_cast(value), size); -} - -void data_type_traits::bind_value(parameter_binder &binder, size_t index, const char *value, size_t size) -{ - binder.bind(index, value, size); -} - -void data_type_traits::bind_result_value(result_parameter_binder &binder, size_t index, const char *value, size_t size) -{ - binder.bind_result_value(index, const_cast(value), size); -} - -void data_type_traits::read_value(query_result_reader &reader, const char *id, size_t index, char* value, size_t size) -{ - reader.read_value(id, index, value, size); -} - -void data_type_traits::bind_value(parameter_binder &binder, size_t index, char *value, size_t size) -{ - binder.bind(index, value, size); -} - -void data_type_traits::bind_result_value(result_parameter_binder &binder, size_t index, char *value, size_t size) -{ - binder.bind_result_value(index, value, size); -} - -void data_type_traits::read_value(query_result_reader &reader, const char *id, size_t index, std::string &value, size_t size) -{ - reader.read_value(id, index, value, size); -} - -void data_type_traits::bind_value(parameter_binder &binder, size_t index, std::string &value, size_t size) -{ - binder.bind(index, value, size); -} - -void data_type_traits::bind_result_value(result_parameter_binder &binder, size_t index, std::string &value, size_t size) -{ - binder.bind_result_value(index, value, size); -} - -void data_type_traits::read_value(query_result_reader &reader, const char *id, size_t index, utils::blob &value) -{ - -} - -void data_type_traits::bind_value(parameter_binder &binder, size_t index, utils::blob &value) -{ - -} - -void data_type_traits::bind_result_value(result_parameter_binder &binder, size_t index, utils::blob &value) -{ - -} -} \ No newline at end of file diff --git a/src/sql/dialect.cpp b/src/sql/dialect.cpp deleted file mode 100644 index 16e6da2..0000000 --- a/src/sql/dialect.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "matador/sql/dialect.hpp" - -#include "matador/utils/string.hpp" - -namespace matador::sql { - -const std::string& dialect::token_at(dialect::token_t token) const -{ - return tokens_.at(token); -} - -const std::string &dialect::data_type_at(data_type_t type) const -{ - return data_types_.at(type); -} - -std::string dialect::prepare_identifier(const column &col) const -{ - std::string result; - if (!col.is_function()) { - if (!col.table.empty()) { - result = prepare_identifier_string(col.table) + "."; - } - result += prepare_identifier_string(col.name); - } else { - result = sql_func_map_.at(col.function_) + "(" + col.name + ")"; - } - if (!col.alias.empty()) { - result += " AS " + col.alias; - } - return result; -} - -std::string dialect::prepare_identifier_string(const std::string &col) const -{ - auto parts = utils::split(col, '.'); - - for (auto &part : parts) { - escape_quotes_in_identifier(part); - quote_identifier(part); - } - return utils::join(parts, "."); -} - -std::string dialect::prepare_literal(const std::string &str) const -{ - std::string result(str); - escape_quotes_in_literals(result); - return result; -} - -void dialect::quote_identifier(std::string &str) const -{ - // str.insert(0, token_at(token_t::START_QUOTE)); - // str += token_at(token_t::END_QUOTE); -} - -void dialect::escape_quotes_in_identifier(std::string &str) const -{ - const std::string open_char(token_at(token_t::START_QUOTE)); - std::string close_char(token_at(token_t::END_QUOTE)); - if (identifier_escape_type() == escape_identifier_t::ESCAPE_CLOSING_BRACKET) { - utils::replace_all(str, close_char, close_char + close_char); - } else { - utils::replace_all(str, open_char, open_char + open_char); - } -} - -void dialect::escape_quotes_in_literals(std::string &str) const -{ - const std::string single_quote(token_at(token_t::STRING_QUOTE)); - const std::string double_quote(token_at(token_t::STRING_QUOTE) + token_at(token_t::STRING_QUOTE)); - utils::replace_all(str, single_quote, double_quote); -} - -dialect::escape_identifier_t dialect::identifier_escape_type() const -{ - return dialect::escape_identifier_t::ESCAPE_BOTH_SAME; -} - -std::string dialect::next_placeholder(const std::vector &bind_vars) const -{ - return placeholder_func_(bind_vars.size()); -} - -std::string dialect::default_schema_name() const -{ - return default_schema_name_; -} - -} \ No newline at end of file diff --git a/src/sql/entity_query_builder.cpp b/src/sql/entity_query_builder.cpp deleted file mode 100644 index b1ff7bc..0000000 --- a/src/sql/entity_query_builder.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "matador/sql/entity_query_builder.hpp" - -namespace matador::sql { - -void entity_query_builder::on_primary_key(const char *id, std::string &, size_t) -{ - push(id); - if (!is_root_entity()) { - auto b = pk_.is_varchar(); - std::cout << "is matching primary key: " << std::boolalpha << b << "\n"; - } -} - -void entity_query_builder::on_revision(const char *id, unsigned long long &/*rev*/) -{ - push(id); -} - -void entity_query_builder::push(const std::string &column_name) -{ - char str[4]; - snprintf(str, 4, "c%02d", ++column_index); - entity_query_data_.columns.emplace_back(table_info_stack_.top().name, column_name, str); -} - -[[nodiscard]] bool entity_query_builder::is_root_entity() const { - return table_info_stack_.size() == 1; -} - -void entity_query_builder::append_join(const column &left, const column &right) -{ - entity_query_data_.joins.push_back({ - { right.table }, - make_condition(left == right) - }); -} - -} \ No newline at end of file diff --git a/src/sql/key_value_pair.cpp b/src/sql/key_value_pair.cpp deleted file mode 100644 index 22e53f8..0000000 --- a/src/sql/key_value_pair.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include - -#include "matador/sql/key_value_pair.hpp" - -namespace matador::sql { - -key_value_pair::key_value_pair(std::string name, any_type value) -: name_(std::move(name)) -, value_(std::move(value)) { -} - -key_value_pair::key_value_pair(const column &col, any_type value) -: name_(col.name) -, value_(std::move(value)) { -} - -key_value_pair::key_value_pair(const char *name, any_type value) - : name_(name) - , value_(std::move(value)) { -} - -const std::string &key_value_pair::name() const { - return name_; -} - -const any_type& key_value_pair::value() const { - return value_; -} -} \ No newline at end of file diff --git a/src/sql/noop_connection.cpp b/src/sql/noop_connection.cpp deleted file mode 100644 index 5985273..0000000 --- a/src/sql/noop_connection.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "matador/sql/noop_connection.hpp" -#include "matador/sql/query_context.hpp" -#include "matador/sql/record.hpp" - -#include -#include - -namespace matador::sql { -noop_connection::noop_connection(const connection_info &info) -: connection_impl(info) {} - -void noop_connection::open() -{ - is_open_ = true; -} - -void noop_connection::close() -{ - is_open_ = false; -} - -bool noop_connection::is_open() -{ - return is_open_; -} - -size_t noop_connection::execute(const std::string &stmt) -{ - return 0; -} - -std::unique_ptr noop_connection::fetch(const std::string &stmt) -{ - return {}; -} - -std::unique_ptr noop_connection::prepare(query_context context) -{ - return {}; -} - -std::vector noop_connection::describe(const std::string &table) -{ - return {}; -} - -bool noop_connection::exists(const std::string &schema_name, const std::string &table_name) -{ - return false; -} - -} \ No newline at end of file diff --git a/src/sql/object_parameter_binder.cpp b/src/sql/object_parameter_binder.cpp deleted file mode 100644 index 933fe1b..0000000 --- a/src/sql/object_parameter_binder.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "matador/sql/object_parameter_binder.hpp" -#include "matador/sql/parameter_binder.hpp" - -namespace matador::sql { - -namespace detail { - -fk_binder::fk_binder(parameter_binder &binder) -: binder_(binder) {} - -void fk_binder::on_primary_key(const char *id, std::string &value, size_t size) -{ - data_type_traits::bind_value(binder_, index_++, value); -} - -} - -object_parameter_binder::object_parameter_binder(parameter_binder &binder) -: binder_(binder) -, fk_binder_(binder) {} - -void object_parameter_binder::reset() -{ - index_ = 0; -} - -void object_parameter_binder::on_primary_key(const char *id, std::string &val, size_t size) -{ - data_type_traits::bind_value(binder_, index_++, val, size); -} - -void object_parameter_binder::on_revision(const char *id, unsigned long long int &rev) -{ - data_type_traits::bind_value(binder_, index_++, rev); -} - -} \ No newline at end of file diff --git a/src/sql/placeholder_generator.cpp b/src/sql/placeholder_generator.cpp deleted file mode 100644 index cc1b5df..0000000 --- a/src/sql/placeholder_generator.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "matador/sql/placeholder_generator.hpp" - -namespace matador::sql { - -void placeholder_generator::on_primary_key(const char *id, std::string &, size_t) -{ - placeholder_values.emplace_back(_); -} - -void placeholder_generator::on_revision(const char *id, unsigned long long int &) -{ - placeholder_values.emplace_back(_); -} - -} \ No newline at end of file diff --git a/src/sql/query.cpp b/src/sql/query.cpp deleted file mode 100644 index 6a8380b..0000000 --- a/src/sql/query.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "matador/sql/query.hpp" -#include "matador/sql/connection.hpp" - -namespace matador::sql { - -query::query(connection &db, const sql::schema &schema) -: connection_(db) -, schema_(schema) -{} - -query_create_intermediate query::create() -{ - return query_create_intermediate(connection_, schema_); -} - -query_drop_intermediate query::drop() -{ - return query_drop_intermediate{connection_, schema_}; -} - -query_select_intermediate query::select(std::initializer_list columns) -{ - return select(std::vector{columns}); -} - -query_select_intermediate query::select(const std::vector& columns) -{ - return {connection_, schema_, columns}; -} - -query_select_intermediate query::select(std::vector columns, std::initializer_list additional_columns) -{ - for (const auto &col : additional_columns) { - columns.push_back(col); - } - return {connection_, schema_, columns}; -} - -query_insert_intermediate query::insert() -{ - return query_insert_intermediate{connection_, schema_}; -} - -query_update_intermediate query::update(const sql::table &table) -{ - return query_update_intermediate{connection_, schema_, table}; -} - -query_delete_intermediate query::remove() -{ - return query_delete_intermediate{connection_, schema_}; -} - -} \ No newline at end of file diff --git a/src/sql/query_builder.cpp b/src/sql/query_builder.cpp deleted file mode 100644 index c867fac..0000000 --- a/src/sql/query_builder.cpp +++ /dev/null @@ -1,537 +0,0 @@ -#include "matador/sql/query_builder.hpp" -#include "matador/sql/column_generator.hpp" -#include "matador/sql/column.hpp" -#include "matador/sql/dialect.hpp" - -#include "matador/utils/string.hpp" - -#include - -namespace matador::sql { - -namespace detail { - -any_type_to_string_visitor::any_type_to_string_visitor(const dialect &d, query_context &query) -: d(d), query(query) -{} - -void any_type_to_string_visitor::to_string(const char *val) -{ - result = "'" + d.prepare_literal(val) + "'"; -} - -void any_type_to_string_visitor::to_string(std::string &val) -{ - result = "'" + d.prepare_literal(val) + "'"; -} - -void any_type_to_string_visitor::to_string(utils::blob &val) -{ - // "This is a binary Data string" as binary data: - // MySQL: X'5468697320697320612062616E617279204461746120737472696E67' - // Postgres: E'\\x5468697320697320612062616E617279204461746120737472696E67' - // MSSQL: 0x5468697320697320612062616E617279204461746120737472696E67 - // Sqlite: X'5468697320697320612062616E617279204461746120737472696E67' - result = d.token_at(dialect::token_t::BEGIN_BINARY_DATA) + utils::to_string(val) + d.token_at(dialect::token_t::END_BINARY_DATA); -} - -void any_type_to_string_visitor::to_string(placeholder &/*val*/) -{ - query.bind_vars.emplace_back("unknown"); - result = d.next_placeholder(query.bind_vars); -} - -} - -// poor mens state machine -// but does the job for query -query_builder::query_state_transition_map query_builder::transitions_{ -{state_t::QUERY_INIT, {state_t::QUERY_CREATE, state_t::QUERY_DROP, state_t::QUERY_SELECT, state_t::QUERY_INSERT, state_t::QUERY_UPDATE, state_t::QUERY_DELETE}}, -{state_t::QUERY_CREATE, {state_t::QUERY_TABLE_CREATE}}, -{state_t::QUERY_DROP, {state_t::QUERY_TABLE_DROP}}, -{state_t::QUERY_SELECT, {state_t::QUERY_FROM}}, -{state_t::QUERY_INSERT, {state_t::QUERY_INTO}}, -{state_t::QUERY_UPDATE, {state_t::QUERY_SET}}, -{state_t::QUERY_DELETE, {state_t::QUERY_FROM}}, -{state_t::QUERY_TABLE_CREATE, {state_t::QUERY_FINISH}}, -{state_t::QUERY_TABLE_DROP, {state_t::QUERY_FINISH}}, -{state_t::QUERY_FROM, {state_t::QUERY_OFFSET, state_t::QUERY_LIMIT, state_t::QUERY_WHERE, state_t::QUERY_ORDER_BY, state_t::QUERY_GROUP_BY, state_t::QUERY_JOIN, state_t::QUERY_FINISH}}, -{state_t::QUERY_SET, {state_t::QUERY_WHERE, state_t::QUERY_FINISH}}, -{state_t::QUERY_JOIN, {state_t::QUERY_ON}}, -{state_t::QUERY_ON, {state_t::QUERY_OFFSET, state_t::QUERY_LIMIT, state_t::QUERY_WHERE, state_t::QUERY_ORDER_BY, state_t::QUERY_GROUP_BY, state_t::QUERY_JOIN, state_t::QUERY_FINISH}}, -{state_t::QUERY_INTO, {state_t::QUERY_VALUES}}, -{state_t::QUERY_WHERE, {state_t::QUERY_ORDER_BY, state_t::QUERY_GROUP_BY, state_t::QUERY_OFFSET, state_t::QUERY_LIMIT, state_t::QUERY_FINISH}}, -{state_t::QUERY_ORDER_BY, {state_t::QUERY_ORDER_DIRECTION}}, -{state_t::QUERY_ORDER_DIRECTION, {state_t::QUERY_OFFSET, state_t::QUERY_LIMIT, state_t::QUERY_FINISH}}, -{state_t::QUERY_GROUP_BY, {state_t::QUERY_ORDER_BY, state_t::QUERY_FINISH}}, -{state_t::QUERY_OFFSET, {state_t::QUERY_LIMIT}}, -{state_t::QUERY_LIMIT, {state_t::QUERY_FINISH}}, -{state_t::QUERY_VALUES, {state_t::QUERY_FINISH}}, -{state_t::QUERY_FINISH, {}}, -}; - -query_builder::query_state_to_string_map query_builder::state_strings_{ -{state_t::QUERY_INIT, "init"}, -{state_t::QUERY_CREATE, "create"}, -{state_t::QUERY_DROP, "drop"}, -{state_t::QUERY_SELECT, "select"}, -{state_t::QUERY_INSERT, "insert"}, -{state_t::QUERY_UPDATE, "update"}, -{state_t::QUERY_DELETE, "delete"}, -{state_t::QUERY_TABLE_CREATE, "table"}, -{state_t::QUERY_TABLE_DROP, "table"}, -{state_t::QUERY_FROM, "from"}, -{state_t::QUERY_SET, "set"}, -{state_t::QUERY_INTO, "into"}, -{state_t::QUERY_WHERE, "where"}, -{state_t::QUERY_ORDER_BY, "order_by"}, -{state_t::QUERY_GROUP_BY, "group_by"}, -{state_t::QUERY_OFFSET, "offset"}, -{state_t::QUERY_LIMIT, "limit"}, -{state_t::QUERY_FINISH, "finish"}, -}; - -query_builder::query_command_to_string_map query_builder::command_strings_{ -{command_t::UNKNOWN, "unknown"}, -{command_t::CREATE, "create"}, -{command_t::DROP, "drop"}, -{command_t::SELECT, "select"}, -{command_t::INSERT, "insert"}, -{command_t::UPDATE, "update"}, -{command_t::REMOVE, "remove"}, -}; - -query_builder::query_builder(const dialect &d) -: dialect_(d), value_to_string_(d, query_) -{} - -query_builder &query_builder::create() -{ - initialize(command_t::CREATE, state_t::QUERY_CREATE); - - query_parts_.emplace_back(dialect::token_t::CREATE, dialect_.token_at(dialect::token_t::CREATE)); - - return *this; -} - -query_builder &query_builder::drop() -{ - initialize(command_t::DROP, state_t::QUERY_DROP); - - query_parts_.emplace_back(dialect::token_t::DROP, dialect_.token_at(dialect::token_t::DROP)); - - return *this; -} - -query_builder &query_builder::select(std::initializer_list columns) -{ - return select(std::vector{columns}); -} - -query_builder &query_builder::select(const std::vector &columns) -{ - initialize(command_t::SELECT, state_t::QUERY_SELECT); - - query_parts_.emplace_back(dialect::token_t::SELECT, dialect_.token_at(dialect::token_t::SELECT) + " "); - - query_.prototype.clear(); - - std::string result; - if (columns.size() < 2) { - for (const auto &col: columns) { - result.append(dialect_.prepare_identifier(col)); - query_.result_vars.emplace_back(col.name); - query_.prototype.emplace_back(col.name); - } - } else { - auto it = columns.begin(); - result.append(dialect_.prepare_identifier(*it)); - query_.result_vars.emplace_back(it->name); - query_.prototype.emplace_back((*it++).name); - for (; it != columns.end(); ++it) { - result.append(", "); - result.append(dialect_.prepare_identifier(*it)); - query_.result_vars.emplace_back(it->name); - query_.prototype.emplace_back((*it).name); - } - } - - query_parts_.emplace_back(dialect::token_t::COLUMNS, result); - return *this; -} - -query_builder &query_builder::insert() -{ - initialize(command_t::INSERT, state_t::QUERY_INSERT); - - query_parts_.emplace_back(dialect::token_t::INSERT, dialect_.token_at(dialect::token_t::INSERT)); - - return *this; -} - -query_builder &query_builder::update(const std::string &table) -{ - initialize(command_t::UPDATE, state_t::QUERY_UPDATE); - - query_.table = {table}; - query_parts_.emplace_back(dialect::token_t::UPDATE, dialect_.token_at(dialect::token_t::UPDATE) + " " + dialect_.prepare_identifier(table)); - - return *this; -} - -query_builder &query_builder::remove() -{ - initialize(command_t::REMOVE, state_t::QUERY_DELETE); - - query_parts_.emplace_back(dialect::token_t::REMOVE, dialect_.token_at(dialect::token_t::REMOVE)); - - return *this; -} - -query_builder &query_builder::table(const std::string &table, std::initializer_list columns) -{ - return this->table(table, std::vector{columns}); -} - -struct fk_context -{ - std::string column; - std::string ref_table; - std::string ref_column; -}; - -struct column_context -{ - std::vector primary_keys; - std::vector foreign_contexts; -}; - -//std::string build_create_column(const column_definition &col, const dialect &d, column_context &context); - -query_builder &query_builder::table(const std::string &table, const std::vector &columns) -{ - transition_to(state_t::QUERY_TABLE_CREATE); - - query_parts_.emplace_back(dialect::token_t::TABLE, " " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table) + " "); - query_.table = {table}; - - std::string result = "("; - - column_context context; - -// if (columns.size() < 2) { -// for (const auto &col: columns) { -// result.append(build_create_column(col, dialect_, context)); -// } -// } else { -// auto it = columns.begin(); -// result.append(build_create_column(*it++, dialect_, context)); -// for (; it != columns.end(); ++it) { -// result.append(", "); -// result.append(build_create_column(*it, dialect_, context)); -// } -// } - - if (!context.primary_keys.empty()) { - result.append(", CONSTRAINT PK_" + table + " PRIMARY KEY (" + utils::join(context.primary_keys, ", ") + ")"); - } - for (const auto &fk: context.foreign_contexts) { - result += ", CONSTRAINT FK_" + table; - result += "_" + fk.column; - result += " FOREIGN KEY (" + fk.column + ")"; - result += " REFERENCES " + fk.ref_table + "(" + fk.ref_column + ")"; - } - - result += ")"; - query_parts_.emplace_back(dialect::token_t::COLUMNS, result); - return *this; -} - -query_builder &query_builder::table(const std::string &table) -{ - transition_to(state_t::QUERY_TABLE_DROP); - - query_parts_.emplace_back(dialect::token_t::TABLE, " " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table)); - query_.table = {table}; - - return *this; -} - -query_builder &query_builder::into(const std::string &table, std::initializer_list column_names) -{ - return into(table, std::vector{column_names}); -} - -query_builder &query_builder::into(const std::string &table, const std::vector &column_names) -{ - transition_to(state_t::QUERY_INTO); - - query_parts_.emplace_back(dialect::token_t::INTO, " " + dialect_.token_at(dialect::token_t::INTO) + " " + dialect_.prepare_identifier(table) + " "); - query_.table = {table}; - - std::string result{"("}; - if (column_names.size() < 2) { - for (const auto &col: column_names) { - result.append(dialect_.prepare_identifier(col.name)); - } - } else { - auto it = column_names.begin(); - result.append(dialect_.prepare_identifier((it++)->name)); - for (; it != column_names.end(); ++it) { - result.append(", "); - result.append(dialect_.prepare_identifier(it->name)); - } - } - result += (")"); - - query_parts_.emplace_back(dialect::token_t::COLUMNS, result); - return *this; -} - -query_builder &query_builder::values(std::initializer_list values) -{ - return this->values(std::vector{values}); -} - -query_builder &query_builder::values(const std::vector &values) -{ - transition_to(state_t::QUERY_VALUES); - - query_parts_.emplace_back(dialect::token_t::VALUES, " " + dialect_.token_at(dialect::token_t::VALUES) + " "); - - std::string result{"("}; - if (values.size() < 2) { - for (auto val: values) { - std::visit(value_to_string_, val); - result.append(value_to_string_.result); - } - } else { - auto it = values.begin(); - auto val = *it++; - std::visit(value_to_string_, val); - result.append(value_to_string_.result); - for (; it != values.end(); ++it) { - result.append(", "); - val = *it; - std::visit(value_to_string_, val); - result.append(value_to_string_.result); - } - } - result += (")"); - - query_parts_.emplace_back(dialect::token_t::INSERT_VALUES, result); - return *this; -} - -query_builder &query_builder::from(const std::string &table, const std::string &as) -{ - transition_to(state_t::QUERY_FROM); - - if (dialect_.default_schema_name().empty()) { - query_parts_.emplace_back(dialect::token_t::FROM, " " + dialect_.token_at(dialect::token_t::FROM) + - " " + dialect_.prepare_identifier(table) + - (as.empty() ? "" : " AS " + dialect_.prepare_identifier(as))); - } else { - query_parts_.emplace_back(dialect::token_t::FROM, " " + dialect_.token_at(dialect::token_t::FROM) + - " " + dialect_.prepare_identifier(dialect_.default_schema_name()) + - "." + dialect_.prepare_identifier(table) + - (as.empty() ? "" : " AS " + dialect_.prepare_identifier(as))); - } - query_.table = {table}; - - return *this; -} - -query_builder &query_builder::join(const std::string &table, join_type_t, const std::string &as) -{ - transition_to(state_t::QUERY_JOIN); - - query_parts_.emplace_back(dialect::token_t::JOIN, " " + dialect_.token_at(dialect::token_t::JOIN) + - " " + dialect_.prepare_identifier(table) + - (as.empty() ? "" : " AS " + dialect_.prepare_identifier(as))); - - return *this; -} - -query_builder &query_builder::on(const std::string &column, const std::string &join_column) -{ - transition_to(state_t::QUERY_ON); - - query_parts_.emplace_back(dialect::token_t::ON, " " + dialect_.token_at(dialect::token_t::ON) + - " " + dialect_.prepare_identifier(column) + - "=" + dialect_.prepare_identifier(join_column)); - - return *this; -} - -query_builder &query_builder::set(std::initializer_list key_values) -{ - return set(std::vector{key_values}); -} - -query_builder &query_builder::set(const std::vector &key_values) -{ - transition_to(state_t::QUERY_SET); - - query_parts_.emplace_back(dialect::token_t::SET, " " + dialect_.token_at(dialect::token_t::SET) + " "); - - std::string result; - if (key_values.size() < 2) { - for (const auto &col: key_values) { - result.append(dialect_.prepare_identifier(col.name()) + "="); - auto var = col.value(); - std::visit(value_to_string_, var); - result.append(value_to_string_.result); - } - } else { - auto it = key_values.begin(); - result.append(dialect_.prepare_identifier(it->name()) + "="); - auto var = (it++)->value(); - std::visit(value_to_string_, var); - result.append(value_to_string_.result); - for (; it != key_values.end(); ++it) { - result.append(", "); - result.append(dialect_.prepare_identifier((*it).name()) + "="); - var = it->value(); - std::visit(value_to_string_, var); - result.append(value_to_string_.result); - } - } - - query_parts_.emplace_back(dialect::token_t::UPDATE_VALUES, result); - return *this; -} - -query_builder &query_builder::where(const basic_condition &cond) -{ - transition_to(state_t::QUERY_WHERE); - - query_parts_.emplace_back(dialect::token_t::WHERE, " " + dialect_.token_at(dialect::token_t::WHERE) + " "); - query_parts_.emplace_back(dialect::token_t::WHERE_CLAUSE, cond.evaluate(const_cast(dialect_), query_)); - - return *this; -} - -query_builder &query_builder::order_by(const std::string &column) -{ - transition_to(state_t::QUERY_ORDER_BY); - - query_parts_.emplace_back(dialect::token_t::ORDER_BY, " " + dialect_.token_at(dialect::token_t::ORDER_BY) + " " + dialect_.prepare_identifier(column)); - - return *this; -} - -query_builder &query_builder::group_by(const std::string &column) -{ - transition_to(state_t::QUERY_GROUP_BY); - - query_parts_.emplace_back(dialect::token_t::GROUP_BY, " " + dialect_.token_at(dialect::token_t::GROUP_BY) + " " + dialect_.prepare_identifier(column)); - - return *this; -} - -query_builder &query_builder::asc() -{ - transition_to(state_t::QUERY_ORDER_DIRECTION); - - query_parts_.emplace_back(dialect::token_t::ASC, " " + dialect_.token_at(dialect::token_t::ASC)); - - return *this; -} - -query_builder &query_builder::desc() -{ - transition_to(state_t::QUERY_ORDER_DIRECTION); - - query_parts_.emplace_back(dialect::token_t::DESC, " " + dialect_.token_at(dialect::token_t::DESC)); - - return *this; -} - -query_builder &query_builder::offset(size_t count) -{ - transition_to(state_t::QUERY_OFFSET); - - query_parts_.emplace_back(dialect::token_t::OFFSET, " " + dialect_.token_at(dialect::token_t::OFFSET) + " " + std::to_string(count)); - - return *this; -} - -query_builder &query_builder::limit(size_t count) -{ - transition_to(state_t::QUERY_LIMIT); - - query_parts_.emplace_back(dialect::token_t::LIMIT, " " + dialect_.token_at(dialect::token_t::LIMIT) + " " + std::to_string(count)); - - return *this; -} - -query_context query_builder::compile() -{ - for (const auto &part: query_parts_) { - query_.sql.append(part.part); - } - query_.command_name = command_strings_[command_]; - return query_; -} - -void query_builder::transition_to(query_builder::state_t next) -{ - if (transitions_[state_].count(next) == 0) { - throw std::logic_error("invalid next state " + state_strings_[next]); - } - state_ = next; -} - -void query_builder::initialize(query_builder::command_t cmd, query_builder::state_t state) -{ - command_ = cmd; - query_ = {}; - state_ = state; - query_parts_.clear(); -} - -//std::string build_create_column(const column_definition &col, const dialect &d, column_context &context) -//{ -// std::string result = d.prepare_identifier(col.name()) + " " + d.data_type_at(col.type()); -// if (col.attributes().size() > 0) { -// result.append("(" + std::to_string(col.attributes().size()) + ")"); -// } -// if (!col.is_nullable()) { -// result.append(" NOT NULL"); -// } -// if (is_constraint_set(col.attributes().options(), utils::constraints::UNIQUE)) { -// result.append(" UNIQUE"); -// } -// if (is_constraint_set(col.attributes().options(), utils::constraints::PRIMARY_KEY)) { -// context.primary_keys.emplace_back(col.name()); -// } -// if (is_constraint_set(col.attributes().options(), utils::constraints::FOREIGN_KEY)) { -// context.foreign_contexts.push_back({col.name(), col.ref_table(), col.ref_column()}); -// } -// -// return result; -//} - -column alias(const std::string &column, const std::string &as) -{ - return {"", column, as}; -} - -column alias(column &&col, const std::string &as) -{ - col.as(as); - return std::move(col); -} - -column count(const std::string &column) -{ - return {sql_function_t::COUNT, column}; -} - -column count_all() -{ - return count("*"); -} - -} \ No newline at end of file diff --git a/src/sql/query_compiler.cpp b/src/sql/query_compiler.cpp deleted file mode 100644 index 3b61102..0000000 --- a/src/sql/query_compiler.cpp +++ /dev/null @@ -1,324 +0,0 @@ -#include "matador/sql/query_compiler.hpp" -#include "matador/sql/query_data.hpp" -#include "matador/sql/column_definition.hpp" -#include "matador/sql/dialect.hpp" -#include "matador/sql/any_type_to_string_visitor.hpp" - -#include "matador/utils/string.hpp" - -namespace matador::sql { - -query_compiler::query_compiler(const sql::dialect &d) -: dialect_(d) -{} - -query_context query_compiler::compile(const query_data *data) -{ - for (const auto &part: data->parts) { - part->accept(*this); - } - - return query_; -} - -void query_compiler::visit(query_select_part &select_part) -{ - query_.sql = dialect_.token_at(dialect::token_t::SELECT) + " "; - - query_.prototype.clear(); - - std::string result; - const auto &columns = select_part.columns(); - if (columns.size() < 2) { - for (const auto &col: columns) { - result.append(dialect_.prepare_identifier(col)); - query_.result_vars.emplace_back(col.name); - query_.prototype.emplace_back(col.name); - } - } else { - auto it = columns.begin(); - result.append(dialect_.prepare_identifier(*it)); - query_.result_vars.emplace_back(it->name); - query_.prototype.emplace_back((*it++).name); - for (; it != columns.end(); ++it) { - result.append(", "); - result.append(dialect_.prepare_identifier(*it)); - query_.result_vars.emplace_back(it->name); - query_.prototype.emplace_back((*it).name); - } - } - - query_.sql += result; -} - -void query_compiler::visit(query_from_part &from_part) -{ - query_.table = from_part.table(); - if (!dialect_.default_schema_name().empty()) { - query_.sql += " " + dialect_.token_at(dialect::token_t::FROM) + - " " + dialect_.prepare_identifier(from_part.table().name) + - (from_part.table().alias.empty() ? "" : " AS " + - dialect_.prepare_identifier(from_part.table().alias)); - } else { - query_.sql += " " + dialect_.token_at(dialect::token_t::FROM) + - " " + dialect_.prepare_identifier(dialect_.default_schema_name()) + - "." + dialect_.prepare_identifier(from_part.table().name) + - (from_part.table().alias.empty() ? "" : " AS " + - dialect_.prepare_identifier(from_part.table().alias)); - } -} - -void query_compiler::visit(query_join_part &join_part) -{ - if (dialect_.default_schema_name().empty()) { - query_.sql += " " + dialect_.token_at(dialect::token_t::JOIN) + - " " + dialect_.prepare_identifier(join_part.table().name) + - (join_part.table().alias.empty() ? "" : " AS " + dialect_.prepare_identifier(join_part.table().alias)); - } else { - query_.sql += " " + dialect_.token_at(dialect::token_t::JOIN) + - " " + dialect_.prepare_identifier(dialect_.default_schema_name()) + - "." + dialect_.prepare_identifier(join_part.table().name) + - (join_part.table().alias.empty() ? "" : " AS " + dialect_.prepare_identifier(join_part.table().alias)); - - } -} - -void query_compiler::visit(query_on_part &on_part) -{ - query_.sql += " " + dialect_.token_at(dialect::token_t::ON) + - " " + on_part.condition().evaluate(const_cast(dialect_), query_); -} - -void query_compiler::visit(query_where_part &where_part) -{ - query_.sql += " " + dialect_.token_at(dialect::token_t::WHERE) + - " " + where_part.condition().evaluate(const_cast(dialect_), query_); -} - -void query_compiler::visit(query_group_by_part &group_by_part) -{ - query_.sql += " " + dialect_.token_at(dialect::token_t::GROUP_BY) + " " + dialect_.prepare_identifier(group_by_part.column()); -} - -void query_compiler::visit(query_order_by_part &order_by_part) -{ - query_.sql += " " + dialect_.token_at(dialect::token_t::ORDER_BY) + - " " + dialect_.prepare_identifier(order_by_part.column()); -} - -void query_compiler::visit(query_order_by_asc_part &order_by_asc_part) -{ - query_.sql += " " + dialect_.token_at(dialect::token_t::ASC); -} - -void query_compiler::visit(query_order_by_desc_part &order_by_desc_part) -{ - query_.sql += " " + dialect_.token_at(dialect::token_t::DESC); -} - -void query_compiler::visit(query_offset_part &offset_part) -{ - query_.sql += " " + dialect_.token_at(dialect::token_t::OFFSET) + " " + std::to_string(offset_part.offset()); -} - -void query_compiler::visit(query_limit_part &limit_part) -{ - query_.sql += " " + dialect_.token_at(dialect::token_t::LIMIT) + " " + std::to_string(limit_part.limit()); -} - -void query_compiler::visit(query_insert_part &insert_part) -{ - query_.sql = dialect_.token_at(dialect::token_t::INSERT); -} - -void query_compiler::visit(query_into_part &into_part) -{ - query_.table = into_part.table(); - query_.sql += " " + dialect_.token_at(dialect::token_t::INTO) + - " " + dialect_.prepare_identifier(into_part.table().name); - - std::string result{"("}; - if (into_part.columns().size() < 2) { - for (const auto &col: into_part.columns()) { - result.append(dialect_.prepare_identifier(col.name)); - } - } else { - auto it = into_part.columns().begin(); - result.append(dialect_.prepare_identifier((it++)->name)); - for (; it != into_part.columns().end(); ++it) { - result.append(", "); - result.append(dialect_.prepare_identifier(it->name)); - } - } - result += (")"); - query_.sql += " " + result; -} - -void query_compiler::visit(query_values_part &values_part) -{ - query_.sql += " " + dialect_.token_at(dialect::token_t::VALUES); - - any_type_to_string_visitor value_to_string(dialect_, query_); - - std::string result{"("}; - if (values_part.values().size() < 2) { - for (auto val: values_part.values()) { - std::visit(value_to_string, val); - result.append(value_to_string.result); - } - } else { - auto it = values_part.values().begin(); - auto val = *it++; - std::visit(value_to_string, val); - result.append(value_to_string.result); - for (; it != values_part.values().end(); ++it) { - result.append(", "); - val = *it; - std::visit(value_to_string, val); - result.append(value_to_string.result); - } - } - result += (")"); - - query_.sql += " " + result; -} - -void query_compiler::visit(query_update_part &update_part) -{ - query_.table = update_part.table(); - query_.sql = dialect_.token_at(dialect::token_t::UPDATE) + " " + dialect_.prepare_identifier(update_part.table().name); -} - -void query_compiler::visit(query_delete_part &delete_part) -{ - query_.sql = dialect_.token_at(dialect::token_t::REMOVE); -} - -void query_compiler::visit(query_delete_from_part &delete_from_part) -{ - query_.table = delete_from_part.table(); - query_.sql += " " + dialect_.token_at(delete_from_part.token()) + - " " + dialect_.prepare_identifier(delete_from_part.table().name); -} - -void query_compiler::visit(query_create_part &create_part) -{ - query_.sql = dialect_.token_at(dialect::token_t::CREATE); -} - -struct fk_context -{ - std::string column; - std::string ref_table; - std::string ref_column; -}; - -struct column_context -{ - std::vector primary_keys; - std::vector foreign_contexts; -}; - -std::string build_create_column(const column_definition &col, const dialect &d, column_context &context); - -void query_compiler::visit(query_create_table_part &create_table_part) -{ - query_.sql += " " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(create_table_part.table().name) + " "; - query_.table = create_table_part.table(); - - std::string result = "("; - - column_context context; - - if (create_table_part.columns().size() < 2) { - for (const auto &col: create_table_part.columns()) { - result.append(build_create_column(col, dialect_, context)); - } - } else { - auto it = create_table_part.columns().begin(); - result.append(build_create_column(*it++, dialect_, context)); - for (; it != create_table_part.columns().end(); ++it) { - result.append(", "); - result.append(build_create_column(*it, dialect_, context)); - } - } - - if (!context.primary_keys.empty()) { - result.append(", CONSTRAINT PK_" + create_table_part.table().name + " PRIMARY KEY (" + utils::join(context.primary_keys, ", ") + ")"); - } - for (const auto &fk: context.foreign_contexts) { - result += ", CONSTRAINT FK_" + create_table_part.table().name; - result += "_" + fk.column; - result += " FOREIGN KEY (" + fk.column + ")"; - result += " REFERENCES " + fk.ref_table + "(" + fk.ref_column + ")"; - } - - result += ")"; - query_.sql += result; -} - -void query_compiler::visit(query_drop_part &drop_part) -{ - query_.sql = dialect_.token_at(dialect::token_t::DROP); -} - -void query_compiler::visit(query_set_part &set_part) -{ - query_.sql += " " + dialect_.token_at(dialect::token_t::SET) + " "; - - any_type_to_string_visitor value_to_string(dialect_, query_); - std::string result; - if (set_part.key_values().size() < 2) { - for (const auto &col: set_part.key_values()) { - result.append(dialect_.prepare_identifier(col.name()) + "="); - auto var = col.value(); - std::visit(value_to_string, var); - result.append(value_to_string.result); - } - } else { - auto it = set_part.key_values().begin(); - result.append(dialect_.prepare_identifier(it->name()) + "="); - auto var = (it++)->value(); - std::visit(value_to_string, var); - result.append(value_to_string.result); - for (; it != set_part.key_values().end(); ++it) { - result.append(", "); - result.append(dialect_.prepare_identifier((*it).name()) + "="); - var = it->value(); - std::visit(value_to_string, var); - result.append(value_to_string.result); - } - } - - query_.sql += result; -} - -void query_compiler::visit(query_drop_table_part &drop_table_part) -{ - query_.sql += " " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(drop_table_part.table().name); - query_.table = drop_table_part.table(); -} - -std::string build_create_column(const column_definition &col, const dialect &d, column_context &context) -{ - std::string result = d.prepare_identifier(col.name()) + " " + d.data_type_at(col.type()); - if (col.attributes().size() > 0) { - result.append("(" + std::to_string(col.attributes().size()) + ")"); - } - if (!col.is_nullable()) { - result.append(" NOT NULL"); - } - if (is_constraint_set(col.attributes().options(), utils::constraints::UNIQUE)) { - result.append(" UNIQUE"); - } - if (is_constraint_set(col.attributes().options(), utils::constraints::PRIMARY_KEY)) { - context.primary_keys.emplace_back(col.name()); - } - if (is_constraint_set(col.attributes().options(), utils::constraints::FOREIGN_KEY)) { - context.foreign_contexts.push_back({col.name(), col.ref_table(), col.ref_column()}); - } - - return result; -} - -} \ No newline at end of file diff --git a/src/sql/query_intermediates.cpp b/src/sql/query_intermediates.cpp deleted file mode 100644 index 62663f7..0000000 --- a/src/sql/query_intermediates.cpp +++ /dev/null @@ -1,289 +0,0 @@ -#include "matador/sql/query_intermediates.hpp" -#include "matador/sql/session.hpp" -#include "matador/sql/query_compiler.hpp" -#include "matador/sql/condition.hpp" - -namespace matador::sql { -basic_query_intermediate::basic_query_intermediate(connection &db, const sql::schema & schema) -: connection_(db) -, schema_(schema) {} - -query_result query_select::fetch_all() -{ - query_compiler compiler(connection_.dialect()); - return connection_.fetch(compiler.compile(data_.get())); -} - -std::optional query_select::fetch_one() -{ - query_compiler compiler(connection_.dialect()); - auto result = connection_.fetch(compiler.compile(data_.get())); - auto first = result.begin(); - if (first == result.end()) { - return std::nullopt; - } - - return *first.get(); -} - -query_context query_select::build() const -{ - query_compiler compiler(connection_.dialect()); - return compiler.compile(data_.get()); -} - -std::unique_ptr query_select::fetch() -{ - query_compiler compiler(connection_.dialect()); - return connection_.fetch(compiler.compile(data_.get()).sql); -} - -statement query_select::prepare() -{ - query_compiler compiler(connection_.dialect()); - return connection_.prepare(compiler.compile(data_.get())); -} - -query_intermediate::query_intermediate(connection &db, const sql::schema &schema, const std::shared_ptr &data) -: basic_query_intermediate(db, schema), data_(data) {} - -query_offset_intermediate query_limit_intermediate::offset(size_t offset) -{ - data_->parts.push_back(std::make_unique(offset)); - return {connection_, schema_, data_}; -} - -query_limit_intermediate query_offset_intermediate::limit(size_t limit) -{ - data_->parts.push_back(std::make_unique(limit)); - return {connection_, schema_, data_}; -} - -query_limit_intermediate query_order_direction_intermediate::limit(size_t limit) -{ - data_->parts.push_back(std::make_unique(limit)); - return {connection_, schema_, data_}; -} - -query_order_by_intermediate query_group_by_intermediate::order_by(const column &col) -{ - data_->parts.push_back(std::make_unique(col)); - return {connection_, schema_, data_}; -} - -query_order_direction_intermediate query_order_by_intermediate::asc() -{ - data_->parts.push_back(std::make_unique()); - return {connection_, schema_, data_}; -} - -query_order_direction_intermediate query_order_by_intermediate::desc() -{ - data_->parts.push_back(std::make_unique()); - return {connection_, schema_, data_}; -} - -query_where_intermediate query_from_intermediate::where_clause(std::unique_ptr &&cond) -{ - if (cond) { - data_->parts.push_back(std::make_unique(std::move(cond))); - } - return {connection_, schema_, data_}; -} - -query_group_by_intermediate query_from_intermediate::group_by(const column &col) -{ - data_->parts.push_back(std::make_unique(col)); - return {connection_, schema_, data_}; -} - -query_order_by_intermediate query_from_intermediate::order_by(const column &col) -{ - data_->parts.push_back(std::make_unique(col)); - return {connection_, schema_, data_}; -} - -query_group_by_intermediate query_where_intermediate::group_by(const column &col) -{ - data_->parts.push_back(std::make_unique(col)); - return {connection_, schema_, data_}; -} - -query_order_by_intermediate query_where_intermediate::order_by(const column &col) -{ - data_->parts.push_back(std::make_unique(col)); - return {connection_, schema_, data_}; -} - -query_on_intermediate query_join_intermediate::on_clause(std::unique_ptr &&cond) -{ - data_->parts.push_back(std::make_unique(std::move(cond))); - return {connection_, schema_, data_}; -} - -query_join_intermediate query_from_intermediate::join_left(const table &t) -{ - data_->parts.push_back(std::make_unique(t)); - return {connection_, schema_, data_}; -} - -query_from_intermediate query_from_intermediate::join_left(join_data &data) -{ - data_->parts.push_back(std::make_unique(data.join_table)); - data_->parts.push_back(std::make_unique(std::move(data.condition))); - return {connection_, schema_, data_}; -} - -query_from_intermediate query_from_intermediate::join_left(std::vector &data_vector) -{ - for (auto &data : data_vector) { - data_->parts.push_back(std::make_unique(data.join_table)); - data_->parts.push_back(std::make_unique(std::move(data.condition))); - } - return {connection_, schema_, data_}; -} - -query_select_intermediate::query_select_intermediate(connection &db, const sql::schema &schema, const std::vector& columns) -: query_start_intermediate(db, schema) -{ - data_->parts.push_back(std::make_unique(columns)); -} - -query_from_intermediate query_select_intermediate::from(const table& t) -{ - data_->parts.push_back(std::make_unique(t)); - return {connection_, schema_, data_}; -} - -query_insert_intermediate::query_insert_intermediate(connection &db, const sql::schema &schema) -: query_start_intermediate(db, schema) -{ - data_->parts.push_back(std::make_unique()); -} - -query_into_intermediate query_insert_intermediate::into(const sql::table &table, std::initializer_list column_names) -{ - return into(table, std::move(std::vector{column_names})); -} - -query_into_intermediate query_insert_intermediate::into(const table &table, std::vector &&column_names) -{ - data_->parts.push_back(std::make_unique(table, column_names)); - return {connection_, schema_, data_}; -} - -query_into_intermediate query_insert_intermediate::into(const table &table) -{ - data_->parts.push_back(std::make_unique(table, table.columns)); - return {connection_, schema_, data_}; -} - -size_t query_execute::execute() -{ - query_compiler compiler(connection_.dialect()); - return connection_.execute(compiler.compile(data_.get()).sql); -} - -statement query_execute::prepare() -{ - query_compiler compiler(connection_.dialect()); - return connection_.prepare(compiler.compile(data_.get())); -} - -query_context query_execute::build() const -{ - query_compiler compiler(connection_.dialect()); - return compiler.compile(data_.get()); -} - -query_execute query_into_intermediate::values(std::initializer_list values) -{ - return this->values(std::vector(values)); -} - -query_execute query_into_intermediate::values(std::vector &&values) -{ - data_->parts.push_back(std::make_unique(std::move(values))); - return {connection_, schema_, data_}; -} - -query_create_intermediate::query_create_intermediate(connection &db, const sql::schema &schema) -: query_start_intermediate(db, schema) { - data_->parts.push_back(std::make_unique()); -} - -query_execute query_create_intermediate::table(const sql::table &table, std::initializer_list columns) -{ - return this->table(table, std::vector{columns}); -} - -query_execute query_create_intermediate::table(const sql::table &table, const std::vector &columns) -{ - data_->parts.push_back(std::make_unique(table, columns)); - return {connection_, schema_, data_}; -} - -query_drop_intermediate::query_drop_intermediate(connection &db, const sql::schema &schema) -: query_start_intermediate(db, schema) -{ - data_->parts.push_back(std::make_unique()); -} - -query_execute query_drop_intermediate::table(const sql::table &table) -{ - data_->parts.push_back(std::make_unique(table)); - return {connection_, schema_, data_}; -} - -query_order_by_intermediate query_execute_where_intermediate::order_by(const column &col) -{ - data_->parts.push_back(std::make_unique(col)); - return {connection_, schema_, data_}; -} - -query_execute_where_intermediate query_set_intermediate::where_clause(std::unique_ptr &&cond) -{ - data_->parts.push_back(std::make_unique(std::move(cond))); - return {connection_, schema_, data_}; -} - -query_update_intermediate::query_update_intermediate(connection &db, const sql::schema &schema, const sql::table& table) -: query_start_intermediate(db, schema) -{ - data_->parts.push_back(std::make_unique(table)); -} - -query_set_intermediate query_update_intermediate::set(std::initializer_list columns) -{ - return set(std::vector{columns}); -} - -query_set_intermediate query_update_intermediate::set(std::vector &&columns) -{ - data_->parts.push_back(std::make_unique(std::move(columns))); - return {connection_, schema_, data_}; - -} - -query_execute_where_intermediate query_delete_from_intermediate::where_clause(std::unique_ptr &&cond) -{ - data_->parts.push_back(std::make_unique(std::move(cond))); - return {connection_, schema_, data_}; -} - -query_delete_intermediate::query_delete_intermediate(connection &db, const sql::schema &schema) -: query_start_intermediate(db, schema) -{ - data_->parts.push_back(std::make_unique()); -} - -query_delete_from_intermediate query_delete_intermediate::from(const sql::table &table) -{ - data_->parts.push_back(std::make_unique(table)); - return {connection_, schema_, data_}; -} - -query_start_intermediate::query_start_intermediate(connection &db, const sql::schema &schema) -: basic_query_intermediate(db, schema) -{} -} \ No newline at end of file diff --git a/src/sql/query_part.cpp b/src/sql/query_part.cpp deleted file mode 100644 index 8e52e60..0000000 --- a/src/sql/query_part.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "matador/sql/query_part.hpp" - -namespace matador::sql { - -query_part::query_part(sql::dialect::token_t token) -: token_(token) {} - -dialect::token_t query_part::token() const -{ - return token_; -} - -} \ No newline at end of file diff --git a/src/sql/query_result_impl.cpp b/src/sql/query_result_impl.cpp deleted file mode 100644 index a21ede0..0000000 --- a/src/sql/query_result_impl.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "matador/sql/query_result_impl.hpp" -#include "matador/sql/query_result_reader.hpp" -#include "matador/sql/value.hpp" - -namespace matador::sql { - -detail::pk_reader::pk_reader(query_result_reader &reader) -: reader_(reader) {} - -void detail::pk_reader::on_primary_key(const char *id, std::string &value, size_t size) -{ - data_type_traits::read_value(reader_, id, column_index_++, value, size); -} - -query_result_impl::query_result_impl(std::unique_ptr &&reader, std::vector prototype) -: prototype_(std::move(prototype)) -, reader_(std::move(reader)) -, pk_reader_(*reader_) -{} - -void query_result_impl::on_primary_key(const char *id, std::string &value, size_t size) -{ - data_type_traits::read_value(*reader_, id, column_index_++, value, size); -} - -void query_result_impl::on_revision(const char *id, unsigned long long int &rev) -{ - data_type_traits::read_value(*reader_, id, column_index_++, rev); - reader_->read_value(id, column_index_++, rev); -} - -void query_result_impl::on_attribute(const char *id, char *value, const utils::field_attributes &attr) -{ - data_type_traits::read_value(*reader_, id, column_index_++, value, attr.size()); -} - -void query_result_impl::on_attribute(const char *id, std::string &value, const utils::field_attributes &attr) -{ - data_type_traits::read_value(*reader_, id, column_index_++, value, attr.size()); -} - -void -query_result_impl::on_attribute(const char *id, value &val, const utils::field_attributes &attr) -{ - reader_->read_value(id, column_index_++, val, attr.size()); -} - -const std::vector& query_result_impl::prototype() const -{ - return prototype_; -} - -} \ No newline at end of file diff --git a/src/sql/query_result_reader.cpp b/src/sql/query_result_reader.cpp deleted file mode 100644 index 79e7328..0000000 --- a/src/sql/query_result_reader.cpp +++ /dev/null @@ -1,189 +0,0 @@ -#include "matador/sql/query_result_reader.hpp" -#include "matador/sql/to_value.hpp" -#include "matador/sql/value.hpp" - -namespace matador::sql { - -void query_result_reader::read_value(const char *id, size_t index, char &value) -{ - sql::to_value(value, column(index)); -} - -void query_result_reader::read_value(const char *id, size_t index, short &value) -{ - sql::to_value(value, column(index)); -} - -void query_result_reader::read_value(const char *id, size_t index, int &value) -{ - sql::to_value(value, column(index)); -} - -void query_result_reader::read_value(const char *id, size_t index, long &value) -{ - sql::to_value(value, column(index)); -} - -void query_result_reader::read_value(const char *id, size_t index, long long int &value) -{ - sql::to_value(value, column(index)); -} - -void query_result_reader::read_value(const char *id, size_t index, unsigned char &value) -{ - sql::to_value(value, column(index)); -} - -void query_result_reader::read_value(const char *id, size_t index, unsigned short &value) -{ - sql::to_value(value, column(index)); -} - -void query_result_reader::read_value(const char *id, size_t index, unsigned int &value) -{ - sql::to_value(value, column(index)); -} - -void query_result_reader::read_value(const char *id, size_t index, unsigned long &value) -{ - sql::to_value(value, column(index)); -} - -void query_result_reader::read_value(const char *id, size_t index, unsigned long long int &value) -{ - sql::to_value(value, column(index)); -} - -void query_result_reader::read_value(const char *id, size_t index, bool &value) -{ - sql::to_value(value, column(index)); -} - -void query_result_reader::read_value(const char *id, size_t index, float &value) -{ - sql::to_value(value, column(index)); -} - -void query_result_reader::read_value(const char *id, size_t index, double &value) -{ - sql::to_value(value, column(index)); -} - -void query_result_reader::read_value(const char *id, size_t index, char *value, size_t size) -{ - auto val = column(index); - size_t len = strlen(val); - if (len > size) { -#ifdef _MSC_VER - strncpy_s(value, size, val, len); -#else - strncpy(value, val, size); -#endif - value[size-1] = '\n'; - } else { -#ifdef _MSC_VER - strcpy_s(value, size, val); -#else - strcpy(value, val); -#endif - } -} - -void query_result_reader::read_value(const char *id, size_t index, std::string &value) -{ - value.assign(column(index)); -} - -void query_result_reader::read_value(const char *id, size_t index, std::string &value, size_t s) -{ - value.assign(column(index)); -} - -void query_result_reader::read_value(const char *id, size_t index, utils::blob &value) -{ - -} - -template < typename Type > -void convert(const char *valstr, value &val) -{ - Type local_val{}; - sql::to_value(local_val, valstr); - val = local_val; -} - -void query_result_reader::read_value(const char *id, size_t index, value &val, size_t size) -{ - switch (val.type()) { - case sql::data_type_t::type_char: - convert(column(index), val); - break; - case sql::data_type_t::type_short: - convert(column(index), val); - break; - case sql::data_type_t::type_int: - convert(column(index), val); - break; - case sql::data_type_t::type_long: - convert(column(index), val); - break; - case sql::data_type_t::type_long_long: - convert(column(index), val); - break; - case sql::data_type_t::type_unsigned_char: - convert(column(index), val); - break; - case sql::data_type_t::type_unsigned_short: - convert(column(index), val); - break; - case sql::data_type_t::type_unsigned_int: - convert(column(index), val); - break; - case sql::data_type_t::type_unsigned_long: - convert(column(index), val); - break; - case sql::data_type_t::type_unsigned_long_long: - convert(column(index), val); - break; - case sql::data_type_t::type_float: - convert(column(index), val); - break; - case sql::data_type_t::type_double: - convert(column(index), val); - break; - case sql::data_type_t::type_bool: { - int local_val{}; - sql::to_value(local_val, column(index)); - val = local_val > 0; - break; - } - case sql::data_type_t::type_text: - case sql::data_type_t::type_varchar: { - val = std::string{column(index)}; - break; - } - case sql::data_type_t::type_char_pointer: { - val = column(index); - break; - } - case sql::data_type_t::type_time: - case sql::data_type_t::type_date: { - val = std::string{column(index)}; - break; - } - case sql::data_type_t::type_null: { - val = nullptr_t{}; - break; - } - case sql::data_type_t::type_blob: { - val = utils::blob{}; - break; - } - case sql::data_type_t::type_unknown: { - val = std::string(column(index)); - break; - } - } -} - -} \ No newline at end of file diff --git a/src/sql/result_parameter_binder.cpp b/src/sql/result_parameter_binder.cpp deleted file mode 100644 index adb90f6..0000000 --- a/src/sql/result_parameter_binder.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "matador/sql/result_parameter_binder.hpp" - -namespace matador::sql { - -namespace detail { -fk_result_binder::fk_result_binder(result_parameter_binder &result_binder) -: result_binder_(result_binder) -{} - -void fk_result_binder::on_primary_key(const char * /*id*/, std::string &value, size_t size) -{ - data_type_traits::bind_result_value(result_binder_, column_index_++, value, size); -} - -} - -void result_parameter_binder::on_primary_key(const char * /*id*/, std::string &value, size_t size) -{ - data_type_traits::bind_result_value(*this, column_index_++, value, size); -} - -void result_parameter_binder::on_revision(const char * /*id*/, unsigned long long &rev) -{ - data_type_traits::bind_result_value(*this, column_index_++, rev); -} - -void result_parameter_binder::on_attribute(const char * /*id*/, char *value, const utils::field_attributes &attr) -{ - data_type_traits::bind_result_value(*this, column_index_++, value, attr.size()); -} - -void result_parameter_binder::on_attribute(const char * /*id*/, std::string &value, const utils::field_attributes &attr) -{ - data_type_traits::bind_result_value(*this, column_index_++, value, attr.size()); -} - -void result_parameter_binder::on_attribute(const char * /*id*/, any_type &value, data_type_t type, const utils::field_attributes &attr) -{ - -} - -} \ No newline at end of file diff --git a/src/sql/schema.cpp b/src/sql/schema.cpp deleted file mode 100644 index b2e857d..0000000 --- a/src/sql/schema.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "matador/sql/schema.hpp" -#include "matador/sql/connection.hpp" - -#include - -namespace matador::sql { - -schema::schema(std::string name) -: name_(std::move(name)) {} - -void schema::create(connection &c) { - for (const auto &ti : repository_) { - // ti.second.prototype - - } -} - -std::string schema::name() const -{ - return name_; -} - -const table_info& schema::attach(const std::type_index ti, const table_info& table) -{ - - auto &ref = repository_.try_emplace(ti, table).first->second; - repository_by_name_.try_emplace(ref.name, std::ref(ref)); - return ref; -} - -std::optional schema::info(std::type_index ti) const -{ - const auto it = repository_.find(ti); - if (it == repository_.end()) { - return std::nullopt; - } - return it->second; -} - -std::optional schema::info(const std::string &name) const -{ - const auto it = repository_by_name_.find(name); - if (it == repository_by_name_.end()) { - return std::nullopt; - } - return it->second; -} - -std::pair schema::reference(const std::type_index &ti) const -{ - const auto it = repository_.find(ti); - if (it != repository_.end()) { - if (!it->second.prototype.has_primary_key()) { - throw std::logic_error("table doesn't has primary key"); - } - return { it->second.name, it->second.prototype.primary_key().value().name() }; - } - - return {}; -} - -bool schema::exists(const std::type_index &ti) const -{ - return repository_.count(ti) > 0; -} - -schema::iterator schema::begin() -{ - return repository_.begin(); -} - -schema::const_iterator schema::begin() const -{ - return repository_.begin(); -} - -schema::iterator schema::end() -{ - return repository_.end(); -} - -schema::const_iterator schema::end() const -{ - return repository_.end(); -} - -bool schema::empty() const -{ - return repository_.empty(); -} - -} \ No newline at end of file diff --git a/src/sql/session.cpp b/src/sql/session.cpp deleted file mode 100644 index af5b319..0000000 --- a/src/sql/session.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include "matador/sql/session.hpp" - -#include "matador/sql/backend_provider.hpp" - -#include - -namespace matador::sql { - -session::session(connection_pool &pool) -: pool_(pool) -, dialect_(backend_provider::instance().connection_dialect(pool_.info().type)) -, schema_(std::make_unique(dialect_.default_schema_name())){} - -void session::create_schema() -{ - auto c = pool_.acquire(); - for (const auto &t : *schema_) { - c->query(*schema_).create().table(t.second.name, t.second.prototype.columns()).execute(); - } -} - -void session::drop_table(const std::string &table_name) -{ - auto c = pool_.acquire(); - if (!c.valid()) { - throw std::logic_error("no database connection available"); - } - - c->query(*schema_).drop().table(table_name).execute(); -} - -query_result session::fetch(const query_context &q) const -{ - auto c = pool_.acquire(); - if (!c.valid()) { - throw std::logic_error("no database connection available"); - } - auto it = prototypes_.find(q.table.name); - if (it == prototypes_.end()) { - it = prototypes_.emplace(q.table.name, c->describe(q.table.name)).first; - } - // adjust columns from given query - for (auto &col : q.prototype) { - if (const auto rit = it->second.find(col.name()); col.type() == data_type_t::type_unknown && rit != it->second.end()) { - const_cast(col).type(rit->type()); - } - } - auto res = c->fetch(q.sql); - return query_result{std::move(res), q.prototype}; -} - -//query_result session::fetch(const std::string &sql) const -//{ -// return query_result(std::unique_ptr()); -//} - -size_t session::execute(const std::string &sql) const { - auto c = pool_.acquire(); - if (!c.valid()) { - throw std::logic_error("no database connection available"); - } - return c->execute(sql); -} - -statement session::prepare(query_context q) const -{ - auto c = pool_.acquire(); - if (!c.valid()) { - throw std::logic_error("no database connection available"); - } - return c->prepare(std::move(q)); -} - -std::vector session::describe_table(const std::string &table_name) const -{ - auto c = pool_.acquire(); - if (!c.valid()) { - throw std::logic_error("no database connection available"); - } - return c->describe(table_name); -} - -bool session::table_exists(const std::string &table_name) const -{ - auto c = pool_.acquire(); - if (!c.valid()) { - throw std::logic_error("no database connection available"); - } - return c->exists(dialect_.default_schema_name(), table_name); -} - -const class dialect &session::dialect() const -{ - return dialect_; -} - -std::unique_ptr session::fetch(const std::string &sql) const -{ - auto c = pool_.acquire(); - if (!c.valid()) { - throw std::logic_error("no database connection available"); - } - return c->fetch(sql); -} - -query_select session::build_select_query(connection_ptr &conn, entity_query_data &&data) const -{ - return conn->query(*schema_) - .select(data.columns) - .from(data.root_table_name) - .join_left(data.joins) - .where(std::move(data.where_clause)) - .order_by({data.root_table_name, data.pk_column_}) - .asc(); -} - -} \ No newline at end of file diff --git a/src/sql/statement.cpp b/src/sql/statement.cpp deleted file mode 100644 index 9e251d4..0000000 --- a/src/sql/statement.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "matador/sql/statement.hpp" - -namespace matador::sql { - -statement::statement(std::unique_ptr impl, const utils::logger &logger) -: statement_(std::move(impl)) -, logger_(logger) -, object_binder_(statement_->binder()) -{} - -statement &statement::bind(size_t pos, const char *value) -{ - statement_->bind(pos, value, 0); - return *this; -} - -statement &statement::bind(size_t pos, std::string &val, size_t size) -{ - statement_->bind(pos, val, size); - return *this; -} - -size_t statement::execute() -{ - logger_.info(statement_->query_.sql); - return statement_->execute(); -} - -query_result statement::fetch() -{ - logger_.info(statement_->query_.sql); - return query_result(statement_->fetch()); -} - -void statement::reset() -{ - statement_->reset(); -} - -} \ No newline at end of file diff --git a/src/sql/statement_cache.cpp b/src/sql/statement_cache.cpp deleted file mode 100644 index b249147..0000000 --- a/src/sql/statement_cache.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "matador/sql/connection.hpp" -#include "matador/sql/statement_cache.hpp" - -namespace matador::sql { -statement &statement_cache::acquire(query_context &&context, const connection &conn) { - std::lock_guard guard(mutex_); - auto key = hash_(context.sql); - auto it = statement_map_.find(key); - if (it == statement_map_.end()) { - cache_info info {conn.prepare(std::move(context)), 1}; -// it = statement_map_.emplace(key, {conn.prepare(std::move(context))}); - } - return it->second.statement_; -} - -void statement_cache::release(const statement &stmt) { - -} -} \ No newline at end of file diff --git a/src/sql/statement_impl.cpp b/src/sql/statement_impl.cpp deleted file mode 100644 index da32133..0000000 --- a/src/sql/statement_impl.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "matador/sql/statement_impl.hpp" - -namespace matador::sql { - -statement_impl::statement_impl(query_context query) -: query_(std::move(query)) -{} - -void statement_impl::bind(size_t pos, const char *value, size_t size) -{ - data_type_traits::bind_value(binder(), pos, value, size); -} - -void statement_impl::bind(size_t pos, std::string &val, size_t size) -{ - data_type_traits::bind_value(binder(), pos, val, size); -} - -} \ No newline at end of file diff --git a/src/sql/table_definition.cpp b/src/sql/table_definition.cpp deleted file mode 100644 index 1fc7e6a..0000000 --- a/src/sql/table_definition.cpp +++ /dev/null @@ -1,148 +0,0 @@ -#include "matador/sql/table_definition.hpp" - -namespace matador::sql { -table_definition::table_definition(std::initializer_list columns) -: columns_(columns) -{ - init(); -} - -table_definition::table_definition(const std::vector &columns) -: columns_(columns) -{ - init(); -} - -table_definition::table_definition(const table_definition &x) -: columns_(x.columns_) -, pk_index_(x.pk_index_) -{ - for (auto& col : columns_) { - add_to_map(col, col.index()); - } -} - -table_definition &table_definition::operator=(const table_definition &x) -{ - if (&x == this) { - return *this; - } - - columns_ = x.columns_; - columns_by_name_.clear(); - pk_index_ = x.pk_index_; - for (auto& col : columns_) { - add_to_map(col, col.index()); - } - return *this; -} - -bool table_definition::has_primary_key() const -{ - return pk_index_ > -1; -} - -std::optional table_definition::primary_key() const -{ - if (!has_primary_key()) { - return std::nullopt; - } - - return columns_[pk_index_]; -} - -void table_definition::append(column_definition col) -{ - auto &ref = columns_.emplace_back(std::move(col)); - add_to_map(ref, columns_.size()-1); -} - -const std::vector &table_definition::columns() const -{ - return columns_; -} - -const column_definition &table_definition::at(const column &col) const -{ - return columns_by_name_.at(col.name).first; -} - -const column_definition &table_definition::at(size_t index) const -{ - return columns_.at(index); -} - -table_definition::iterator table_definition::find(const std::string &column_name) -{ - auto it = columns_by_name_.find(column_name); - return it != columns_by_name_.end() ? columns_.begin() + it->second.second : columns_.end(); -} - -table_definition::const_iterator table_definition::find(const std::string &column_name) const { - auto it = columns_by_name_.find(column_name); - return it != columns_by_name_.end() ? columns_.begin() + it->second.second : columns_.end(); -} - -table_definition::iterator table_definition::begin() -{ - return columns_.begin(); -} - -table_definition::const_iterator table_definition::begin() const -{ - return columns_.begin(); -} - -table_definition::const_iterator table_definition::cbegin() const -{ - return columns_.cbegin(); -} - -table_definition::iterator table_definition::end() -{ - return columns_.end(); -} - -table_definition::const_iterator table_definition::end() const -{ - return columns_.end(); -} - -table_definition::const_iterator table_definition::cend() const -{ - return columns_.cend(); -} - -size_t table_definition::size() const -{ - return columns_.size(); -} - -bool table_definition::empty() const -{ - return columns_.empty(); -} - -void table_definition::clear() -{ - columns_.clear(); - columns_by_name_.clear(); -} - -void table_definition::init() -{ - size_t index{0}; - for(auto &col : columns_) { - add_to_map(col, index++); - } -} - -void table_definition::add_to_map(column_definition &col, size_t index) -{ - columns_by_name_.emplace(col.name(), column_index_pair {std::ref(col), index}); - if (utils::is_constraint_set(col.attributes().options(), utils::constraints::PRIMARY_KEY)) { - pk_index_ = static_cast(index); - } -} - -} \ No newline at end of file diff --git a/src/sql/value.cpp b/src/sql/value.cpp deleted file mode 100644 index 13bcff9..0000000 --- a/src/sql/value.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include "matador/sql/value.hpp" - -#include - -namespace matador::sql { -namespace detail { - -size_t determine_size(const std::string &val) -{ - return val.size(); -} - -size_t determine_size(const char *val) -{ - return strlen(val); -} - -size_t determine_size(const utils::blob &val) -{ - return val.size(); -} - -} -value::value(data_type_t data_type, size_t size) -: size_(size) -, type_(data_type) {} - -value::value(value &&x) noexcept - : value_(std::move(x.value_)) - , type_(x.type_) -{ - x.value_ = nullptr; - x.type_ = data_type_t::type_unknown; -} - -value &value::operator=(value &&x) noexcept -{ - value_ = std::move(x.value_); - type_ = x.type_; - x.value_ = nullptr; - x.type_ = data_type_t::type_unknown; - - return *this; -} - -std::string value::str() const -{ - return as().value(); -} - -size_t value::size() const -{ - return size_; -} - -data_type_t value::type() const -{ - return type_; -} - -bool value::is_integer() const -{ - return type_ >= data_type_t::type_char && type_ <= data_type_t::type_unsigned_long_long; -} - -bool value::is_floating_point() const -{ - return type_ == data_type_t::type_float || type_ == data_type_t::type_double; -} - -bool value::is_bool() const -{ - return type_ == data_type_t::type_bool; -} - -bool value::is_string() const -{ - return type_ == data_type_t::type_text; -} - -bool value::is_varchar() const -{ - return type_ == data_type_t::type_varchar || type_ == data_type_t::type_char_pointer; -} - -bool value::is_blob() const -{ - return type_ == data_type_t::type_blob; -} - -bool value::is_null() const -{ - return type_ == data_type_t::type_null; -} - -bool value::is_unknown() const -{ - return type_ == data_type_t::type_unknown; -} - -} \ No newline at end of file diff --git a/src/sql/value_extractor.cpp b/src/sql/value_extractor.cpp deleted file mode 100644 index 2000f16..0000000 --- a/src/sql/value_extractor.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "matador/sql/value_extractor.hpp" - -namespace matador::sql { - -value_extractor::value_extractor(std::vector &values) -: values_(values) -{} - -void value_extractor::on_primary_key(const char *, std::string &pk, size_t) -{ - append(pk); -} - -void value_extractor::on_revision(const char *, unsigned long long int &rev) -{ - append(rev); -} - -void value_extractor::on_attribute(const char *, char *x, const utils::field_attributes &) -{ - append(x); -} - -void value_extractor::on_attribute(const char *, std::string &x, const utils::field_attributes &) -{ - append(x); -} - -} \ No newline at end of file diff --git a/src/utils/field_attributes.cpp b/src/utils/field_attributes.cpp deleted file mode 100644 index 4ab2d57..0000000 --- a/src/utils/field_attributes.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "matador/utils/field_attributes.hpp" - -namespace matador::utils { - -field_attributes::field_attributes(size_t size) - : size_(size) -{} - -field_attributes::field_attributes(constraints options) - : options_(options) -{} - -field_attributes::field_attributes(size_t size, constraints options) - : size_(size) - , options_(options) -{} - -size_t field_attributes::size() const -{ - return size_; -} - -void field_attributes::size(size_t size) -{ - size_ = size; -} - -constraints field_attributes::options() const -{ - return options_; -} - -} \ No newline at end of file diff --git a/src/utils/foreign_attributes.cpp b/src/utils/foreign_attributes.cpp deleted file mode 100644 index c2a1e4a..0000000 --- a/src/utils/foreign_attributes.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "matador/utils/foreign_attributes.hpp" - -matador::utils::foreign_attributes::foreign_attributes(matador::utils::cascade_type cascade) -: cascade_(cascade) {} - -matador::utils::foreign_attributes::foreign_attributes(fetch_type fetch) -: fetch_(fetch) {} - -matador::utils::foreign_attributes::foreign_attributes(matador::utils::cascade_type cascade, fetch_type fetch) -: cascade_(cascade) -, fetch_(fetch ) {} - -matador::utils::cascade_type matador::utils::foreign_attributes::cascade() const -{ - return cascade_; -} - -matador::utils::fetch_type matador::utils::foreign_attributes::fetch() const -{ - return fetch_; -} - diff --git a/src/utils/logger.cpp b/src/utils/logger.cpp deleted file mode 100644 index f360840..0000000 --- a/src/utils/logger.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#include "matador/utils/logger.hpp" - -#include "matador/utils/os.hpp" -#include "matador/utils/string.hpp" - -#include -#include -#include -#include -#include -#include -#include - -namespace matador::utils { - -std::size_t acquire_thread_index(std::thread::id id); - -std::map level_strings = { /* NOLINT */ -{ log_level::LVL_DEBUG, "DEBUG" }, -{ log_level::LVL_INFO, "INFO" }, -{ log_level::LVL_WARN, "WARN" }, -{ log_level::LVL_ERROR, "ERROR" }, -{ log_level::LVL_TRACE, "TRACE" } -}; - -logger::logger(const std::string &path, std::string source) -: source_(std::move(source)) -{ - std::string filename(path); - // find last dir delimiter - const char *last = strrchr(path.c_str(), os::DIR_SEPARATOR); - if (last != nullptr) { - path_.assign(path.data(), last-path.data()); - } else { - path_.clear(); - } - - if (last != nullptr) { - filename = (last + 1); - } - // extract base path and extension - const auto result = utils::split(filename, '.'); - if (result.size() != 2) { - throw std::logic_error("split path must consists of two elements"); - } - // get current path - auto pwd = os::get_current_dir(); - // make path - os::mkpath(path_); - // change into path - os::chdir(path_); - // create file - stream = os::fopen(filename, "a"); - if (stream == nullptr) { - os::chdir(pwd); - throw std::logic_error("error opening file"); - } - os::chdir(pwd); -} - -logger::logger(FILE *file, std::string source) -: stream(file) -, source_(std::move(source)) -{} - -logger::logger(const logger &x) -: path_(x.path_) -, stream(x.stream) -, source_(x.source_) -{} - -logger &logger::operator=(const logger &x) -{ - if (this == &x) { - return *this; - } - path_ = x.path_; - stream = x.stream; - source_ = x.source_; - return *this; -} - -logger::logger(logger &&x) noexcept -: path_(std::move(x.path_)) -, stream(x.stream) -, source_(std::move(x.source_)) -{ - x.stream = nullptr; -} - -logger &logger::operator=(logger &&x) noexcept -{ - path_ = std::move(x.path_); - std::swap(stream, x.stream); - source_ = std::move(x.source_); - return *this; -} - -void logger::log(log_level lvl, const char *message) const -{ - using namespace std::chrono; - auto timestamp = system_clock::now(); - auto coarse = system_clock::to_time_t(timestamp); - auto fine = time_point_cast(timestamp); - - char timestamp_buffer[sizeof "9999-12-31 23:59:59.999"]; - std::snprintf(timestamp_buffer + std::strftime(timestamp_buffer, - sizeof timestamp_buffer - 3, - "%F %T.", - std::localtime(&coarse)), 4, "%03lld", fine.time_since_epoch().count() % 1000); - char buffer[1024]; - -#ifdef _MSC_VER - int ret = sprintf_s(buffer, 1024, "%s [Thread %zu] [%-7s] [%s]: %s\n", timestamp_buffer, acquire_thread_index(std::this_thread::get_id()), level_strings[lvl].c_str(), source_.c_str(), message); -#else - int ret = sprintf(buffer, "%s [Thread %lu] [%-7s] [%s]: %s\n", timestamp_buffer, acquire_thread_index(std::this_thread::get_id()), level_strings[lvl].c_str(), source_.c_str(), message); -#endif - - std::lock_guard l(mutex_); - write(buffer, ret); -} - -void logger::write(const char *message, size_t size) const -{ - fwrite(message, sizeof(char), size, stream); - fflush(stream); -} - -void logger::close() -{ - if (stream) { - fclose(stream); - stream = nullptr; - } -} - -std::size_t acquire_thread_index(std::thread::id id) -{ - static std::size_t next_index = 0; - static std::mutex my_mutex; - static std::map ids; - std::lock_guard lock(my_mutex); - if(ids.find(id) == ids.end()) { - ids[id] = next_index++; - } - return ids[id]; -} - -} \ No newline at end of file diff --git a/src/utils/os.cpp b/src/utils/os.cpp deleted file mode 100644 index e675d67..0000000 --- a/src/utils/os.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "matador/utils/os.hpp" - -#ifdef _WIN32 -#include -#include -#include -#else -#include -#include -#endif - -#include -#include -#include - -namespace matador::utils::os { - -#ifdef _WIN32 -char DIR_SEPARATOR = '\\'; -const char* DIR_SEPARATOR_STRING = "\\"; -#else -char DIR_SEPARATOR = '/'; -const char* DIR_SEPARATOR_STRING = "/"; -#endif - -std::string getenv(const char *name) { -#ifdef _WIN32 - char var[1024]; - size_t len{}; - const auto error = getenv_s(&len, var, 1024, name); - if (error > 0) { - throw std::logic_error(error_string(error)); - }; - - return var; -#else - char *path = ::getenv(name); - return path == nullptr ? "" : path; -#endif -} - -[[maybe_unused]] std::string getenv(const std::string &name) { - return getenv(name.c_str()); -} - -#ifdef _WIN32 -std::string error_string(unsigned long error) { - char* lpMsgBuf; - auto bufLen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, - error, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &lpMsgBuf, - 0, - nullptr); - std::string result; - if (bufLen) { - result.append(lpMsgBuf, lpMsgBuf+bufLen); - LocalFree(lpMsgBuf); - } - return result; -} -#endif - -std::string get_current_dir() -{ - char buffer[1024]; -#ifdef _WIN32 - char *dir = _getcwd(buffer, 1024); -#else - char *dir = ::getcwd(buffer, 1024); -#endif - if (dir == nullptr) { -#ifdef _WIN32 - ::strerror_s(buffer, 1023, errno); - throw std::logic_error(buffer); -#else - throw std::logic_error(::strerror(errno)); -#endif - } - return {buffer}; -} - -FILE* fopen(const std::string &path, const char *modes) -{ - return fopen(path.c_str(), modes); -} - -FILE* fopen(const char *path, const char *modes) -{ -#ifdef _WIN32 - return _fsopen(path, modes, _SH_DENYWR); -#else - return ::fopen(path, modes); -#endif -} - -bool mkdir(const std::string &dirname) -{ - return os::mkdir(dirname.c_str()); -} - -bool mkdir(const char *dirname) -{ - if (dirname == nullptr || strlen(dirname) == 0) { - return true; - } -#ifdef _WIN32 - return ::_mkdir(dirname) == 0; -#else - return ::mkdir(dirname, S_IRWXU) == 0; -#endif -} - -bool chdir(const std::string &dirname) -{ - return os::chdir(dirname.c_str()); -} - -bool chdir(const char *dirname) -{ - if (dirname == nullptr || strlen(dirname) == 0) { - return true; - } -#ifdef _WIN32 - return _chdir(dirname) == 0; -#else - return ::chdir(dirname) == 0; -#endif -} - -bool rmdir(const std::string &dirname) -{ - return os::rmdir(dirname.c_str()); -} - -bool rmdir(const char *dirname) -{ -#ifdef _WIN32 - return _rmdir(dirname) == 0; -#else - return ::rmdir(dirname) == 0; -#endif -} - -bool mkpath(const std::string &path) { - return os::rmpath(path.c_str()); -} - -bool mkpath(const char *path) { - char tmp[256]; - size_t len; - - snprintf(tmp, sizeof(tmp),"%s",path); - len = strlen(tmp); - if (len == 0) { - return true; - } - if(tmp[len - 1] == DIR_SEPARATOR) { - tmp[len - 1] = 0; - } - for(char *p = tmp + 1; *p; p++) { - if (*p == DIR_SEPARATOR) { - *p = 0; - if (!os::mkdir(tmp)) { - return false; - } - *p = DIR_SEPARATOR; - } - } - return os::mkdir(tmp); -} - -bool rmpath(const std::string &path) -{ - return os::rmpath(path.c_str()); -} - -bool rmpath(const char *path) -{ - // change next to last path segment - std::vector pathcopy(path, path+::strlen(path)+1); - std::vector segments; -#ifdef _WIN32 - char *next_token = nullptr; - char *segment = ::strtok_s(pathcopy.data(), DIR_SEPARATOR_STRING, &next_token); -#else - char *segment = ::strtok(pathcopy.data(), DIR_SEPARATOR_STRING); -#endif - - while (segment != nullptr) { - os::chdir(segment); - segments.emplace_back(segment); -#ifdef _WIN32 - segment = ::strtok_s(nullptr, DIR_SEPARATOR_STRING, &next_token); -#else - segment = ::strtok(nullptr, DIR_SEPARATOR_STRING); -#endif - } - - auto first = segments.rbegin(); - for (auto it=first; it!=segments.rend(); ++it) { - os::chdir(".."); - os::rmdir(*it); - } - return true; -} -} \ No newline at end of file diff --git a/src/utils/string.cpp b/src/utils/string.cpp deleted file mode 100644 index f72851e..0000000 --- a/src/utils/string.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "matador/utils/string.hpp" - -#include - -namespace matador::utils { - -void replace_all(std::string &in, const std::string &from, const std::string &to) -{ - if(from.empty()) { - return; - } - size_t start_pos = 0; - while((start_pos = in.find(from, start_pos)) != std::string::npos) { - in.replace(start_pos, from.length(), to); - start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx' - } -} - -const std::string &to_string(const std::string &str) -{ - return str; -} - -std::string to_string(const blob &data) -{ - static constexpr char HEXITS[] = "0123456789ABCDEF"; - - std::string str(2 * data.size(), '\0'); - auto item = str.begin(); - - for(auto c : data) { - *item++ = HEXITS[c >> 4]; - *item++ = HEXITS[c & 0x0F]; - } - - return str; -} - -std::vector split(const std::string &str, char delim) -{ - std::stringstream ss(str); - std::string item; - std::vector result; - while (std::getline(ss, item, delim)) { - result.push_back(item); - } - return result; -} - -} \ No newline at end of file diff --git a/test/AnyTypeToVisitorTest.cpp b/test/AnyTypeToVisitorTest.cpp deleted file mode 100644 index 2150a7e..0000000 --- a/test/AnyTypeToVisitorTest.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include - -#include "matador/sql/any_type.hpp" -#include "matador/sql/any_type_to_visitor.hpp" - -using namespace matador::sql; - -TEST_CASE("Convert any type to string", "[any type visitor]") { - any_type_to_visitor to_string_visitor; - - any_type value = 6; - std::visit(to_string_visitor, value); - REQUIRE(to_string_visitor.result == "6"); - - value = 2.5; - std::visit(to_string_visitor, value); - REQUIRE(to_string_visitor.result == "2.5"); - - value = true; - std::visit(to_string_visitor, value); - REQUIRE(to_string_visitor.result == "true"); - - value = "hello"; - std::visit(to_string_visitor, value); - REQUIRE(to_string_visitor.result == "hello"); - - value = std::string{"world"}; - std::visit(to_string_visitor, value); - REQUIRE(to_string_visitor.result == "world"); -} - -TEST_CASE("Convert any type to integral", "[any type visitor]") { - any_type_to_visitor to_long_visitor; - - any_type value = 6; - std::visit(to_long_visitor, value); - REQUIRE(to_long_visitor.result == 6); - - value = 2.5; - std::visit(to_long_visitor, value); - REQUIRE(to_long_visitor.result == 2); - - value = true; - std::visit(to_long_visitor, value); - REQUIRE(to_long_visitor.result == 1); - - value = "hello"; - std::visit(to_long_visitor, value); - REQUIRE(to_long_visitor.result == 0); - - value = std::string{"world"}; - std::visit(to_long_visitor, value); - REQUIRE(to_long_visitor.result == 0); -} - -TEST_CASE("Convert any type to floating point", "[any type visitor]") { - any_type_to_visitor to_double_visitor; - - any_type value = 6; - std::visit(to_double_visitor, value); - REQUIRE(to_double_visitor.result == 6); - - value = 2.5; - std::visit(to_double_visitor, value); - REQUIRE(to_double_visitor.result == 2.5); - - value = true; - std::visit(to_double_visitor, value); - REQUIRE(to_double_visitor.result == 1); - - value = "hello"; - std::visit(to_double_visitor, value); - REQUIRE(to_double_visitor.result == 0); - - value = std::string{"world"}; - std::visit(to_double_visitor, value); - REQUIRE(to_double_visitor.result == 0); -} \ No newline at end of file diff --git a/test/BackendProviderTest.cpp b/test/BackendProviderTest.cpp deleted file mode 100644 index e03c2bb..0000000 --- a/test/BackendProviderTest.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include - -#include "matador/sql/connection_info.hpp" -#include "matador/sql/backend_provider.hpp" - -#include "matador/utils/os.hpp" - -using namespace matador::sql; - -TEST_CASE("Load backend", "[backend provider]") { - auto path = matador::utils::os::getenv("MATADOR_BACKENDS_PATH"); - REQUIRE(!path.empty()); - - if (path.back() != '\\') { - path.push_back('\\'); - } - - REQUIRE(!path.empty()); - - connection_info ci{}; - const auto &d = backend_provider::instance().connection_dialect("noop"); - auto *connection = backend_provider::instance().create_connection("noop", ci); - REQUIRE(connection != nullptr); - backend_provider::instance().destroy_connection("noop", connection); -} \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2f823df..7f46932 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,56 +1,4 @@ -Include(FetchContent) +enable_testing() -FetchContent_Declare( - Catch2 - GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v3.5.4 # or a later release -) - -FetchContent_MakeAvailable(Catch2) - -list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) -include(CTest) -include(Catch) - -add_executable(tests - QueryBuilderTest.cpp - TableDefinitionTest.cpp - ConnectionPoolTest.cpp - BackendProviderTest.cpp - models/product.hpp - models/order.hpp - models/order_details.hpp - ColumnDefinitionGeneratorTest.cpp - ColumnGeneratorTest.cpp - ValueGeneratorTest.cpp - models/category.hpp - models/supplier.hpp - models/airplane.hpp - models/flight.hpp - models/person.hpp - AnyTypeToVisitorTest.cpp - ColumnTest.cpp - models/coordinate.hpp - models/location.hpp - models/optional.hpp - ConvertTest.cpp - EntityQueryBuilderTest.cpp - models/author.hpp - models/book.hpp - FieldTest.cpp - models/recipe.hpp - ValueTest.cpp - ResultTest.cpp - utils/auto_reset_event.hpp - utils/auto_reset_event.cpp - models/student.hpp) - -target_link_libraries(tests PRIVATE - Catch2::Catch2WithMain - matador - ${CMAKE_DL_LIBS} - ${SQLite3_LIBRARIES} - ${PostgreSQL_LIBRARY}) -target_include_directories(tests PUBLIC $/include) - -catch_discover_tests(tests) +add_subdirectory(core) +add_subdirectory(orm) \ No newline at end of file diff --git a/test/ColumnDefinitionGeneratorTest.cpp b/test/ColumnDefinitionGeneratorTest.cpp deleted file mode 100644 index d8fd9f3..0000000 --- a/test/ColumnDefinitionGeneratorTest.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include - -#include "matador/sql/column_definition_generator.hpp" -#include "matador/sql/schema.hpp" - -#include "models/product.hpp" -#include "models/optional.hpp" - -using namespace matador::sql; -using namespace matador::utils; - -TEST_CASE("Generate column definitions from object", "[column][definition][generator]") { - schema repo("main"); - - auto columns = column_definition_generator::generate(repo); - - const std::vector expected_columns = { - column_definition{"product_name", data_type_t::type_varchar, constraints::PRIMARY_KEY, null_option::NOT_NULL }, - column_definition{"supplier_id", data_type_t::type_unsigned_long, constraints::FOREIGN_KEY, null_option::NOT_NULL }, - column_definition{"category_id", data_type_t::type_unsigned_long, constraints::FOREIGN_KEY, null_option::NOT_NULL }, - column_definition{"quantity_per_unit", data_type_t::type_varchar, null_attributes, null_option::NOT_NULL }, - column_definition{"unit_price", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL }, - column_definition{"units_in_stock", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL }, - column_definition{"units_in_order", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL }, - column_definition{"reorder_level", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL }, - column_definition{"discontinued", data_type_t::type_bool, null_attributes, null_option::NOT_NULL } - }; - REQUIRE(!columns.empty()); - REQUIRE(columns.size() == expected_columns.size()); - - for (size_t i = 0; i != expected_columns.size(); ++i) { - REQUIRE(expected_columns[i].name() == columns[i].name()); - REQUIRE(expected_columns[i].attributes().options() == columns[i].attributes().options() ); - REQUIRE(expected_columns[i].type() == columns[i].type() ); - } -} - -TEST_CASE("Generate columns from object with nullable columns", "[column generator]") { - schema repo("main"); - - auto columns = column_definition_generator::generate(repo); - - const std::vector expected_columns = { - column_definition{"id", data_type_t::type_unsigned_long, constraints::PRIMARY_KEY, null_option::NOT_NULL }, - column_definition{"name", data_type_t::type_varchar, null_attributes, null_option::NOT_NULL }, - column_definition{"age", data_type_t::type_unsigned_int, null_attributes, null_option::NOT_NULL } - }; - REQUIRE(!columns.empty()); - REQUIRE(columns.size() == expected_columns.size()); - - for (size_t i = 0; i != expected_columns.size(); ++i) { - REQUIRE(expected_columns[i].name() == columns[i].name()); - REQUIRE(expected_columns[i].attributes().options() == columns[i].attributes().options() ); - REQUIRE(expected_columns[i].type() == columns[i].type() ); - } -} \ No newline at end of file diff --git a/test/ColumnGeneratorTest.cpp b/test/ColumnGeneratorTest.cpp deleted file mode 100644 index ecfd5a0..0000000 --- a/test/ColumnGeneratorTest.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include - -#include "matador/sql/column_generator.hpp" -#include "matador/sql/schema.hpp" - -#include "models/order.hpp" -#include "models/product.hpp" -#include "models/book.hpp" -#include "models/author.hpp" - -using namespace matador::sql; - -TEST_CASE("Generate columns from object", "[column][generator]") { - using namespace matador::test; - schema s("main"); - s.attach("product"); - - auto columns = column_generator::generate(s); - - const std::vector expected_columns = { - "product_name", - "supplier_id", - "category_id", - "quantity_per_unit", - "unit_price", - "units_in_stock", - "units_in_order", - "reorder_level", - "discontinued" - }; - REQUIRE(!columns.empty()); - REQUIRE(columns.size() == expected_columns.size()); - - for (size_t i = 0; i != expected_columns.size(); ++i) { - REQUIRE(expected_columns[i] == columns[i].name); - } -} - -TEST_CASE("Generate columns for object with has many relation", "[column][generator][relation]") { - using namespace matador::test; - schema s("main"); - s.attach("product"); - s.attach("order_details"); - s.attach("order"); - - auto columns = column_generator::generate(s); - - const std::vector expected_columns = { - { "order", "order_id", "c01" }, - { "order", "order_date", "c02" }, - { "order", "required_date", "c03" }, - { "order", "shipped_date", "c04" }, - { "order", "ship_via", "c05" }, - { "order", "freight", "c06" }, - { "order", "ship_name", "c07" }, - { "order", "ship_address", "c08" }, - { "order", "ship_city", "c09" }, - { "order", "ship_region", "c10" }, - { "order", "ship_postal_code", "c11" }, - { "order", "ship_country", "c12" }, - { "order_details", "order_details_id", "c13" }, - { "order_details", "order_id", "c14" }, - { "order_details", "product_id", "c15" } - }; - REQUIRE(!columns.empty()); - REQUIRE(columns.size() == expected_columns.size()); - - for (size_t i = 0; i != expected_columns.size(); ++i) { - REQUIRE(expected_columns[i].equals(columns[i])); - } - -} - -TEST_CASE("Generate columns for object with eager foreign key relation", "[column][generator][eager]") { - using namespace matador::test; - schema s("main"); - s.attach("books"); - s.attach("authors"); - - const std::vector expected_columns { - { "books", "id", "c01" }, - { "books", "title", "c02" }, - { "authors", "id", "c03" }, - { "authors", "first_name", "c04" }, - { "authors", "last_name", "c05" }, - { "authors", "date_of_birth", "c06" }, - { "authors", "year_of_birth", "c07" }, - { "authors", "distinguished", "c08" }, - { "books", "published_in", "c09" } - }; - auto columns = column_generator::generate(s); - - REQUIRE(!columns.empty()); - REQUIRE(columns.size() == expected_columns.size()); - for (size_t i = 0; i != expected_columns.size(); ++i) { - REQUIRE(expected_columns[i].equals(columns[i])); - } -} diff --git a/test/ConnectionPoolTest.cpp b/test/ConnectionPoolTest.cpp deleted file mode 100644 index 7860320..0000000 --- a/test/ConnectionPoolTest.cpp +++ /dev/null @@ -1,154 +0,0 @@ -#include - -#include "matador/sql/connection_pool.hpp" -#include "matador/sql/noop_connection.hpp" - -#include "utils/auto_reset_event.hpp" - -using namespace matador::sql; -using namespace matador::test::utils; - -TEST_CASE("Create connection pool", "[connection pool]") { - using pool_t = connection_pool; - - pool_t pool("noop://noop.db", 4); - - REQUIRE(pool.size() == 4); - REQUIRE(pool.idle() == 4); - REQUIRE(pool.inuse() == 0); - - auto ptr = pool.acquire(); - REQUIRE(ptr.valid()); - REQUIRE(ptr.id().value() > 0); - REQUIRE(ptr->is_open()); - - REQUIRE(pool.idle() == 3); - REQUIRE(pool.inuse() == 1); - - pool.release(ptr); - REQUIRE(!ptr.valid()); - REQUIRE(pool.idle() == 4); - REQUIRE(pool.inuse() == 0); - - ptr = pool.acquire(3); - REQUIRE(ptr.valid()); - REQUIRE(ptr.id() == 3); - REQUIRE(ptr->is_open()); - { - auto ptr2 = pool.acquire(); - REQUIRE(ptr2.valid()); - REQUIRE(ptr2->is_open()); - - REQUIRE(pool.idle() == 2); - REQUIRE(pool.inuse() == 2); - } - - REQUIRE(pool.idle() == 3); - REQUIRE(pool.inuse() == 1); - - pool.release(ptr); - REQUIRE(!ptr.valid()); - REQUIRE(pool.idle() == 4); - REQUIRE(pool.inuse() == 0); -} - -TEST_CASE("Acquire connection by id", "[connection pool]") { - using pool_t = connection_pool; - - pool_t pool("noop://noop.db", 4); - - REQUIRE(pool.size() == 4); - REQUIRE(pool.idle() == 4); - REQUIRE(pool.inuse() == 0); - - auto ptr = pool.acquire(); - REQUIRE(ptr.valid()); - REQUIRE(ptr.id()); - REQUIRE(ptr.id().value() > 0); - REQUIRE(ptr->is_open()); - - auto same_ptr = pool.acquire(ptr.id().value()); - - REQUIRE(!same_ptr.valid()); - - const auto connection_id = ptr.id().value(); - - pool.release(ptr); - REQUIRE(!ptr.valid()); - - same_ptr = pool.acquire(connection_id); - - REQUIRE(same_ptr.valid()); - REQUIRE(same_ptr.id() == connection_id); -} - -TEST_CASE("Try acquire connection", "[connection pool][try acquire]") { - using pool_t = connection_pool; - - pool_t pool("noop://noop.db", 1); - - REQUIRE(pool.size() == 1); - REQUIRE(pool.idle() == 1); - REQUIRE(pool.inuse() == 0); - - auto ptr = pool.try_acquire(); - REQUIRE(ptr.valid()); - REQUIRE(ptr.id()); - REQUIRE(ptr.id().value() > 0); - REQUIRE(ptr->is_open()); - REQUIRE(pool.size() == 1); - REQUIRE(pool.idle() == 0); - REQUIRE(pool.inuse() == 1); - - auto ptr2 = pool.try_acquire(); - REQUIRE(!ptr2.valid()); - - pool.release(ptr); - REQUIRE(!ptr.valid()); - REQUIRE(pool.size() == 1); - REQUIRE(pool.idle() == 1); - REQUIRE(pool.inuse() == 0); - - ptr2 = pool.try_acquire(); - REQUIRE(ptr2.valid()); - REQUIRE(ptr2.id()); - REQUIRE(ptr2.id().value() > 0); - REQUIRE(ptr2->is_open()); - REQUIRE(pool.size() == 1); - REQUIRE(pool.idle() == 0); - REQUIRE(pool.inuse() == 1); - - pool.release(ptr2); - - auto_reset_event reset_main_event; - auto_reset_event reset_thread_event; - - std::thread t([&reset_main_event, &reset_thread_event, &pool]() { - auto c1 = pool.acquire(); - REQUIRE(c1.valid()); - REQUIRE(c1.id()); - REQUIRE(c1.id().value() > 0); - - reset_main_event.set(); - - reset_thread_event.wait_one(); - - pool.release(c1); - REQUIRE(!c1.valid()); - reset_main_event.set(); - }); - reset_main_event.wait_one(); - - ptr2 = pool.try_acquire(); - REQUIRE(!ptr2.valid()); - - reset_thread_event.set(); - - reset_main_event.wait_one(); - ptr2 = pool.try_acquire(); - REQUIRE(ptr2.valid()); - REQUIRE(ptr2.id()); - REQUIRE(ptr2.id().value() > 0); - - t.join(); -} \ No newline at end of file diff --git a/test/ConvertTest.cpp b/test/ConvertTest.cpp deleted file mode 100644 index 0b72330..0000000 --- a/test/ConvertTest.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include - -#include "matador/sql/convert.hpp" - -using namespace matador::sql; - -TEST_CASE("Test convert function", "[convert]") { - -} \ No newline at end of file diff --git a/test/EntityQueryBuilderTest.cpp b/test/EntityQueryBuilderTest.cpp deleted file mode 100644 index 235bf19..0000000 --- a/test/EntityQueryBuilderTest.cpp +++ /dev/null @@ -1,258 +0,0 @@ -#include - -#include -#include - -#include "models/airplane.hpp" -#include "models/author.hpp" -#include "models/book.hpp" -#include "models/flight.hpp" -#include "models/recipe.hpp" -#include "models/order.hpp" -#include "models/student.hpp" - -using namespace matador::sql; - -TEST_CASE("Create sql query data for entity with eager has one", "[query][entity][builder]") { - using namespace matador::test; - connection db("noop://noop.db"); - schema scm("noop"); - scm.attach("airplanes"); - scm.attach("flights"); - - entity_query_builder eqb(scm); - - auto data = eqb.build(17); - - REQUIRE(data.is_ok()); - REQUIRE(data->root_table_name == "flights"); - REQUIRE(data->joins.size() == 1); - const std::vector expected_columns { - { "flights", "id", "c01" }, - { "airplanes", "id", "c02" }, - { "airplanes", "brand", "c03" }, - { "airplanes", "model", "c04" }, - { "flights", "pilot_name", "c05" }, - }; - REQUIRE(data->columns.size() == expected_columns.size()); - for (size_t i = 0; i != expected_columns.size(); ++i) { - REQUIRE(expected_columns[i].equals(data->columns[i])); - } - - std::vector> expected_join_data { - { "airplanes", R"("flights"."airplane_id" = "airplanes"."id")"} - }; - - query_context qc; - size_t index{0}; - for (const auto &jd : data->joins) { - REQUIRE(jd.join_table.name == expected_join_data[index].first); - REQUIRE(jd.condition->evaluate(db.dialect(), qc) == expected_join_data[index].second); - ++index; - } - - REQUIRE(data->where_clause); - auto cond = data->where_clause->evaluate(db.dialect(), qc); - REQUIRE(cond == R"("flights"."id" = 17)"); -} - -TEST_CASE("Create sql query data for entity with eager belongs to", "[query][entity][builder]") { - using namespace matador::test; - connection db("noop://noop.db"); - schema scm("noop"); - scm.attach("authors"); - scm.attach("books"); - - entity_query_builder eqb(scm); - - auto data = eqb.build(17); - - REQUIRE(data.is_ok()); - REQUIRE(data->root_table_name == "books"); - REQUIRE(data->joins.size() == 1); - const std::vector expected_columns { - { "books", "id", "c01" }, - { "books", "title", "c02" }, - { "authors", "id", "c03" }, - { "authors", "first_name", "c04" }, - { "authors", "last_name", "c05" }, - { "authors", "date_of_birth", "c06" }, - { "authors", "year_of_birth", "c07" }, - { "authors", "distinguished", "c08" }, - { "books", "published_in", "c09" } - }; - REQUIRE(data->columns.size() == expected_columns.size()); - for (size_t i = 0; i != expected_columns.size(); ++i) { - REQUIRE(expected_columns[i].equals(data->columns[i])); - } - - std::vector> expected_join_data { - { "authors", R"("books"."author_id" = "authors"."id")"} - }; - - query_context qc; - size_t index{0}; - for (const auto &jd : data->joins) { - REQUIRE(jd.join_table.name == expected_join_data[index].first); - REQUIRE(jd.condition->evaluate(db.dialect(), qc) == expected_join_data[index].second); - ++index; - } - - REQUIRE(data->where_clause); - auto cond = data->where_clause->evaluate(db.dialect(), qc); - REQUIRE(cond == R"("books"."id" = 17)"); - - auto q = db.query(scm) - .select(data->columns) - .from(data->root_table_name); - - for (auto &jd : data->joins) { - q.join_left(jd.join_table) - .on(std::move(jd.condition)); - } - auto context = q - .where(std::move(data->where_clause)) - .build(); -} - -TEST_CASE("Create sql query data for entity with eager has many belongs to", "[query][entity][builder]") { - using namespace matador::test; - connection db("noop://noop.db"); - schema scm("noop"); - scm.attach("products"); - scm.attach("order_details"); - scm.attach("orders"); - - entity_query_builder eqb(scm); - - auto data = eqb.build(17); - - REQUIRE(data.is_ok()); - REQUIRE(data->root_table_name == "orders"); - REQUIRE(data->joins.size() == 1); - const std::vector expected_columns = { - { "orders", "order_id", "c01" }, - { "orders", "order_date", "c02" }, - { "orders", "required_date", "c03" }, - { "orders", "shipped_date", "c04" }, - { "orders", "ship_via", "c05" }, - { "orders", "freight", "c06" }, - { "orders", "ship_name", "c07" }, - { "orders", "ship_address", "c08" }, - { "orders", "ship_city", "c09" }, - { "orders", "ship_region", "c10" }, - { "orders", "ship_postal_code", "c11" }, - { "orders", "ship_country", "c12" }, - { "order_details", "order_details_id", "c13" }, - { "order_details", "order_id", "c14" }, - { "order_details", "product_id", "c15" } - }; - REQUIRE(data->columns.size() == expected_columns.size()); - for (size_t i = 0; i != expected_columns.size(); ++i) { - REQUIRE(expected_columns[i].equals(data->columns[i])); - } - - std::vector> expected_join_data { - { "order_details", R"("orders"."order_id" = "order_details"."order_id")"} - }; - - query_context qc; - size_t index{0}; - for (const auto &jd : data->joins) { - REQUIRE(jd.join_table.name == expected_join_data[index].first); - REQUIRE(jd.condition->evaluate(db.dialect(), qc) == expected_join_data[index].second); - ++index; - } - - REQUIRE(data->where_clause); - auto cond = data->where_clause->evaluate(db.dialect(), qc); - REQUIRE(cond == R"("orders"."order_id" = 17)"); -} - -TEST_CASE("Create sql query data for entity with eager many to many", "[query][entity][builder]") { - using namespace matador::test; - connection db("noop://noop.db"); - schema scm("noop"); - scm.attach("recipes"); - scm.attach("ingredients"); - scm.attach("recipe_ingredients"); - - entity_query_builder eqb(scm); - - auto data = eqb.build(17); - - REQUIRE(data.is_ok()); - REQUIRE(data->root_table_name == "ingredients"); - REQUIRE(data->joins.size() == 2); - const std::vector expected_columns { - { "ingredients", "id", "c01" }, - { "ingredients", "name", "c02" }, - { "recipes", "id", "c03" }, - { "recipes", "name", "c04" } - }; - REQUIRE(data->columns.size() == expected_columns.size()); - for (size_t i = 0; i != expected_columns.size(); ++i) { - REQUIRE(expected_columns[i].equals(data->columns[i])); - } - - std::vector> expected_join_data { - { "recipe_ingredients", R"("ingredients"."id" = "recipe_ingredients"."ingredient_id")"}, - { "recipes", R"("recipe_ingredients"."recipe_id" = "recipes"."id")"} - }; - - query_context qc; - size_t index{0}; - for (const auto &jd : data->joins) { - REQUIRE(jd.join_table.name == expected_join_data[index].first); - REQUIRE(jd.condition->evaluate(db.dialect(), qc) == expected_join_data[index].second); - ++index; - } - - REQUIRE(data->where_clause); - auto cond = data->where_clause->evaluate(db.dialect(), qc); - REQUIRE(cond == R"("ingredients"."id" = 17)"); -} - -TEST_CASE("Create sql query data for entity with eager many to many (inverse part)", "[query][entity][builder]") { - using namespace matador::test; - connection db("noop://noop.db"); - schema scm("noop"); - scm.attach("students"); - scm.attach("courses"); - scm.attach("student_courses"); - - entity_query_builder eqb(scm); - - auto data = eqb.build(17); - - REQUIRE(data.is_ok()); - REQUIRE(data->root_table_name == "courses"); - REQUIRE(data->joins.size() == 2); - const std::vector expected_columns { - { "courses", "id", "c01" }, - { "courses", "title", "c02" }, - { "students", "id", "c03" }, - { "students", "name", "c04" } - }; - REQUIRE(data->columns.size() == expected_columns.size()); - for (size_t i = 0; i != expected_columns.size(); ++i) { - REQUIRE(expected_columns[i].equals(data->columns[i])); - } - - std::vector> expected_join_data { - { "student_courses", R"("courses"."id" = "student_courses"."course_id")"}, - { "students", R"("student_courses"."student_id" = "students"."id")"} - }; - - query_context qc; - size_t index{0}; - for (const auto &jd : data->joins) { - REQUIRE(jd.join_table.name == expected_join_data[index].first); - REQUIRE(jd.condition->evaluate(db.dialect(), qc) == expected_join_data[index].second); - ++index; - } - - REQUIRE(data->where_clause); - auto cond = data->where_clause->evaluate(db.dialect(), qc); - REQUIRE(cond == R"("courses"."id" = 17)"); -} \ No newline at end of file diff --git a/test/QueryBuilderTest.cpp b/test/QueryBuilderTest.cpp deleted file mode 100644 index e524f86..0000000 --- a/test/QueryBuilderTest.cpp +++ /dev/null @@ -1,250 +0,0 @@ -#include - -#include -#include -#include -#include -#include - -using namespace matador::sql; -using namespace matador::utils; - -TEST_CASE("Create table sql statement string", "[query]") -{ - connection noop("noop://noop.db"); - schema scm("noop"); - query q(noop, scm); - auto result = q.create().table({"person"}, { - make_pk_column("id"), - make_column("name", 255), - make_column("age") - }).build(); - - REQUIRE(result.sql == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL, "age" INTEGER NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id)))##"); - REQUIRE(result.table.name == "person"); - - result = q.create().table("person", { - make_pk_column("id"), - make_column("name", {255, constraints::UNIQUE}, null_option::NOT_NULL), - make_column("age"), - make_fk_column("address", "address", "id") - }).build(); - - REQUIRE(result.sql == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL UNIQUE, "age" INTEGER NOT NULL, "address" BIGINT NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id), CONSTRAINT FK_person_address FOREIGN KEY (address) REFERENCES address(id)))##"); - REQUIRE(result.table.name == "person"); -} - -TEST_CASE("Drop table sql statement string", "[query]") -{ - connection noop("noop://noop.db"); - schema scm("noop"); - query q(noop, scm); - const auto result = q.drop().table("person").build(); - - REQUIRE(result.sql == R"(DROP TABLE "person")"); - REQUIRE(result.table.name == "person"); -} - -TEST_CASE("Select sql statement string", "[query]") -{ - connection noop("noop://noop.db"); - schema scm("noop"); - query q(noop, scm); - const auto result = q.select({"id", "name", "age"}).from("person").build(); - - REQUIRE(result.sql == R"(SELECT "id", "name", "age" FROM "person")"); - REQUIRE(result.table.name == "person"); -} - -TEST_CASE("Insert sql statement string", "[query]") -{ - connection noop("noop://noop.db"); - schema scm("noop"); - query q(noop, scm); - const auto result = q.insert().into("person", { - "id", "name", "age" - }).values({7UL, "george", 65U}).build(); - - REQUIRE(result.sql == R"(INSERT INTO "person" ("id", "name", "age") VALUES (7, 'george', 65))"); - REQUIRE(result.table.name == "person"); -} - -TEST_CASE("Update sql statement string", "[query]") -{ - connection noop("noop://noop.db"); - schema scm("noop"); - query q(noop, scm); - const auto result = q.update("person").set({ - {"id", 7UL}, - {"name", "george"}, - {"age", 65U} - }).build(); - - REQUIRE(result.sql == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65)"); - REQUIRE(result.table.name == "person"); -} - -TEST_CASE("Update limit sql statement", "[query][update][limit]") -{ - connection noop("noop://noop.db"); - schema scm("noop"); - query q(noop, scm); - const auto result = q.update("person") - .set({{"id", 7UL}, {"name", "george"}, {"age", 65U}}) - .where("name"_col == "george") - .order_by("id"_col).asc() - .limit(2) - .build(); - - REQUIRE(result.sql == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65 WHERE "name" = 'george' ORDER BY "id" ASC LIMIT 2)"); - REQUIRE(result.table.name == "person"); -} - -TEST_CASE("Delete sql statement string", "[query]") -{ - connection noop("noop://noop.db"); - schema scm("noop"); - query q(noop, scm); - const auto result = q.remove().from("person").build(); - - REQUIRE(result.sql == R"(DELETE FROM "person")"); - REQUIRE(result.table.name == "person"); -} - -TEST_CASE("Delete limit sql statement", "[query][delete][limit]") -{ - connection noop("noop://noop.db"); - schema scm("noop"); - query q(noop, scm); - const auto result = q.remove() - .from("person") - .where("name"_col == "george") - .order_by("id"_col).asc() - .limit(2) - .build(); - - REQUIRE(result.sql == R"(DELETE FROM "person" WHERE "name" = 'george' ORDER BY "id" ASC LIMIT 2)"); - REQUIRE(result.table.name == "person"); -} - -TEST_CASE("Select sql statement string with where clause", "[query]") -{ - connection noop("noop://noop.db"); - schema scm("noop"); - query q(noop, scm); - auto result = q.select({"id", "name", "age"}) - .from("person") - .where("id"_col == 8 && "age"_col > 50) - .build(); - - REQUIRE(result.sql == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = 8 AND "age" > 50))"); - REQUIRE(result.table.name == "person"); - - result = q.select({"id", "name", "age"}) - .from("person") - .where("id"_col == _ && "age"_col > 50) - .build(); - - REQUIRE(result.sql == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = ? AND "age" > 50))"); - REQUIRE(result.table.name == "person"); -} - -TEST_CASE("Insert sql statement with placeholder", "[query]") -{ - connection noop("noop://noop.db"); - schema scm("noop"); - query q(noop, scm); - const auto result = q.insert().into("person", { - "id", "name", "age" - }).values({_, _, _}).build(); - - REQUIRE(result.sql == R"(INSERT INTO "person" ("id", "name", "age") VALUES (?, ?, ?))"); - REQUIRE(result.table.name == "person"); - REQUIRE(result.bind_vars.size() == 3); -} - -TEST_CASE("Select sql statement string with order by", "[query]") -{ - connection noop("noop://noop.db"); - schema scm("noop"); - query q(noop, scm); - const auto result = q.select({"id", "name", "age"}) - .from("person") - .order_by("name").asc() - .build(); - - REQUIRE(result.sql == R"(SELECT "id", "name", "age" FROM "person" ORDER BY "name" ASC)"); - REQUIRE(result.table.name == "person"); -} - -TEST_CASE("Select sql statement string with group by", "[query]") -{ - connection noop("noop://noop.db"); - schema scm("noop"); - query q(noop, scm); - const auto result = q.select({"id", "name", "age"}) - .from("person") - .group_by("age") - .build(); - - REQUIRE(result.sql == R"(SELECT "id", "name", "age" FROM "person" GROUP BY "age")"); - REQUIRE(result.table.name == "person"); -} - -TEST_CASE("Select sql statement string with offset and limit", "[query]") -{ - connection noop("noop://noop.db"); - schema scm("noop"); - query q(noop, scm); - const auto result = q.select({"id", "name", "age"}) - .from("person") - .order_by("id"_col).asc() - .limit(20) - .offset(10) - .build(); - - REQUIRE(result.sql == R"(SELECT "id", "name", "age" FROM "person" ORDER BY "id" ASC LIMIT 20 OFFSET 10)"); - REQUIRE(result.table.name == "person"); -} - -TEST_CASE("Create, insert and select a blob column", "[query][blob]") -{ - connection noop("noop://noop.db"); - schema scm("noop"); - query q(noop, scm); - auto result = q.create().table("person", { - make_pk_column("id"), - make_column("name", 255), - make_column("data") - }).build(); - - REQUIRE(result.sql == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL, "data" BLOB NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id)))##"); - REQUIRE(result.table.name == "person"); - - result = q.insert().into("person", { - "id", "name", "data" - }).values({7UL, "george", blob{1, 'A', 3, 4}}).build(); - - REQUIRE(result.sql == R"(INSERT INTO "person" ("id", "name", "data") VALUES (7, 'george', X'01410304'))"); - REQUIRE(result.table.name == "person"); - - result = q.select({"id", "name", "data"}).from("person").build(); - - REQUIRE(result.sql == R"(SELECT "id", "name", "data" FROM "person")"); - REQUIRE(result.table.name == "person"); -} - -TEST_CASE("Select statement with join_left", "[query][join_left]") -{ - connection noop("noop://noop.db"); - schema scm("noop"); - query q(noop, scm); - auto result = q.select({"f.id", "ap.brand", "f.pilot_name"}) - .from({"flight", "f"}) - .join_left({"airplane", "ap"}) - .on("f.airplane_id"_col == "ap.id"_col) - .build(); - - REQUIRE(result.sql == R"(SELECT "f"."id", "ap"."brand", "f"."pilot_name" FROM "flight" AS "f" INNER JOIN "airplane" AS "ap" ON "f"."airplane_id" = "ap"."id")"); - REQUIRE(result.table.name == "flight"); -} \ No newline at end of file diff --git a/test/ResultTest.cpp b/test/ResultTest.cpp deleted file mode 100644 index e84d093..0000000 --- a/test/ResultTest.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include - -#include "matador/utils/result.hpp" - -namespace matador::test { - -enum class math_error : int32_t { - DIVISION_BY_ZERO = 1 -}; - -utils::resultdivide(int x, int y) { - if (y == 0) { - return utils::error(math_error::DIVISION_BY_ZERO); - } - - return utils::ok(float(x) / y); -} - -utils::resultmultiply(int x, int y) { - return utils::ok(float(x) * y); -} - -utils::resultplus(int x, int y) { - return utils::ok(float(x) + y); -} - -utils::resulterror_to_string(math_error err) { - switch (err) { - case math_error::DIVISION_BY_ZERO: - return utils::error(std::string("division by zero error")); - default: - return utils::error(std::string("unknown error")); - } -} - -} - -using namespace matador; - -TEST_CASE("Result tests", "[result]") { - auto res = test::divide(4, 2); - REQUIRE(res); - REQUIRE(res.is_ok()); - REQUIRE(!res.is_error()); - - REQUIRE((res.value() == 2.0)); - - REQUIRE_THROWS(res.err()); - - res = test::divide(4, 0); - REQUIRE(!res); - REQUIRE(!res.is_ok()); - REQUIRE(res.is_error()); - - REQUIRE((res.err() == test::math_error::DIVISION_BY_ZERO)); - - res = test::divide(4, 2) - .and_then([](const auto &val) { return test::multiply(val, 5); }) - .and_then([](const auto &val) { return test::plus(val, 10); }); - - REQUIRE(res); - REQUIRE(res.is_ok()); - REQUIRE(!res.is_error()); - REQUIRE((res.value() == 20.0)); - - res = test::divide(4, 0) - .and_then([](const auto &val) { - return test::multiply(val, 5); - }); - - REQUIRE(!res); - REQUIRE(!res.is_ok()); - REQUIRE(res.is_error()); - REQUIRE((res.err() == test::math_error::DIVISION_BY_ZERO)); - - auto res2 = test::divide(4, 0) - .or_else([](const auto &err) { - return test::error_to_string(err); - }); - - REQUIRE(!res2); - REQUIRE(!res2.is_ok()); - REQUIRE(res2.is_error()); - REQUIRE((res2.err() == "division by zero error")); - - res = test::divide(4, 2) - .and_then([](const auto &val) { return test::multiply(val, 5); }) - .transform([](const auto &val) { return val + 10; }); - - REQUIRE(res); - REQUIRE(res.is_ok()); - REQUIRE(!res.is_error()); - REQUIRE((res.value() == 20.0)); - -} \ No newline at end of file diff --git a/test/TableDefinitionTest.cpp b/test/TableDefinitionTest.cpp deleted file mode 100644 index 32298aa..0000000 --- a/test/TableDefinitionTest.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include - -#include - -#include - -using namespace matador::sql; - -TEST_CASE("Create record", "[record]") { - table_definition def({ - make_pk_column("id"), - make_column("name", 255), - make_column("color", 255) - }); - - REQUIRE(def.size() == 3); - - std::list expected_columns = {"id", "name", "color"}; - for(const auto &col : expected_columns) { - REQUIRE(def.find(col) != def.end()); - } - - for(const auto& col : def) { - expected_columns.remove(col.name()); - } - - REQUIRE(expected_columns.empty()); -} - -TEST_CASE("Append to record", "[record]") { - table_definition rec; - - rec.append(make_pk_column("id")); - rec.append("name", 255); - rec.append("color", 63); - - REQUIRE(rec.size() == 3); - - std::list expected_columns = {"id", "name", "color"}; - for(const auto &col : expected_columns) { - REQUIRE(rec.find(col) != rec.end()); - } - - for(const auto& col : rec) { - expected_columns.remove(col.name()); - } - - REQUIRE(expected_columns.empty()); -} \ No newline at end of file diff --git a/test/ValueGeneratorTest.cpp b/test/ValueGeneratorTest.cpp deleted file mode 100644 index a8e1df6..0000000 --- a/test/ValueGeneratorTest.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include - -#include "matador/sql/value_extractor.hpp" - -#include "models/product.hpp" - -using namespace matador::sql; - -TEST_CASE("Extract values object", "[value extractor]") { - matador::test::product p; - p.discontinued = false; - p.reorder_level = 1; - p.units_in_order = 2; - p.units_in_stock = 100; - p.unit_price = 49; - p.quantity_per_unit = "pcs"; - p.category = make_entity(); - p.category->id = 7; - p.supplier = make_entity(); - p.supplier->id = 13; - p.product_name = "candle"; - - const std::vector expected_values { - std::string{"candle"}, 13UL, 7UL, std::string{"pcs"}, 49U, 100U, 2U, 1U, false - }; - auto values = value_extractor::extract(p); - - REQUIRE(values == expected_values); -} \ No newline at end of file diff --git a/test/ValueTest.cpp b/test/ValueTest.cpp deleted file mode 100644 index 48da82f..0000000 --- a/test/ValueTest.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include - -#include "matador/sql/value.hpp" -#include "matador/utils/types.hpp" - -using namespace matador::sql; - -TEST_CASE("Test value class", "[value]") { - value v; - - REQUIRE(v.is_unknown()); - REQUIRE(v.type() == data_type_t::type_unknown); - REQUIRE(v.size() == 0); - - v = 7; - - REQUIRE(v.is_integer()); - REQUIRE(v.type() == data_type_t::type_int); - REQUIRE(v.size() == 0); - REQUIRE(v.as() == 7); - REQUIRE(v.as() == 7); - - v = "test"; - - REQUIRE(v.is_varchar()); - REQUIRE(v.type() == data_type_t::type_char_pointer); - REQUIRE(v.size() == 4); - - v = std::string{"hello"}; - - REQUIRE(v.is_varchar()); - REQUIRE(v.type() == data_type_t::type_varchar); - REQUIRE(v.size() == 5); - - v = 4.5; - - REQUIRE(v.is_floating_point()); - REQUIRE(v.type() == data_type_t::type_double); - REQUIRE(v.size() == 0); - - v = 6.7f; - - REQUIRE(v.is_floating_point()); - REQUIRE(v.type() == data_type_t::type_float); - REQUIRE(v.size() == 0); - - v = std::string(); - - REQUIRE(v.is_string()); - REQUIRE(v.type() == data_type_t::type_text); - REQUIRE(v.size() == 0); - - v = true; - - REQUIRE(v.is_bool()); - REQUIRE(v.type() == data_type_t::type_bool); - REQUIRE(v.size() == 0); - - v = nullptr; - - REQUIRE(v.is_null()); - REQUIRE(v.type() == data_type_t::type_null); - REQUIRE(v.size() == 0); - - v = matador::utils::blob{ 1, 2, 3, 4 }; - - REQUIRE(v.is_blob()); - REQUIRE(v.type() == data_type_t::type_blob); - REQUIRE(v.size() == 4); -} \ No newline at end of file diff --git a/test/backends/ColorEnumTraits.cpp b/test/backends/ColorEnumTraits.cpp new file mode 100644 index 0000000..f94dc23 --- /dev/null +++ b/test/backends/ColorEnumTraits.cpp @@ -0,0 +1,23 @@ +#include "ColorEnumTraits.hpp" + +#include "matador/utils/attribute_writer.hpp" +#include "matador/utils/attribute_reader.hpp" + +namespace matador::utils { + +void data_type_traits::read_value(attribute_reader &reader, const char *id, size_t index, + test::Color &value) +{ + std::string enum_string; + reader.read_value(id, index, enum_string, 64); + if (const auto enum_opt = color_enum.to_enum(enum_string)) { + value = enum_opt.value(); + } +} + +void data_type_traits::bind_value(attribute_writer &binder, const size_t index, const test::Color &value) +{ + binder.write_value(index, color_enum.to_string(value)); +} + +} diff --git a/test/backends/ColorEnumTraits.hpp b/test/backends/ColorEnumTraits.hpp new file mode 100644 index 0000000..74cb7d4 --- /dev/null +++ b/test/backends/ColorEnumTraits.hpp @@ -0,0 +1,28 @@ +#ifndef MATADOR_COLOR_ENUM_TRAITS_HPP +#define MATADOR_COLOR_ENUM_TRAITS_HPP + +#include "matador/utils/default_type_traits.hpp" + +#include "matador/utils/enum_mapper.hpp" + +#include "models/location.hpp" + +static const matador::utils::enum_mapper color_enum({ + {matador::test::Color::Green, "green"}, + {matador::test::Color::Red, "red"}, + {matador::test::Color::Blue, "blue"}, + {matador::test::Color::Yellow, "yellow"}, + {matador::test::Color::Black, "black"}, + {matador::test::Color::White, "white"}, + {matador::test::Color::Brown, "brown"} + }); + +template<> +struct matador::utils::data_type_traits +{ + static basic_type type(const std::size_t size) { return data_type_traits::type(size); } + static void read_value(attribute_reader &reader, const char *id, size_t index, test::Color &value); + static void bind_value(attribute_writer &binder, size_t index, const test::Color &value); +}; + +#endif //MATADOR_COLOR_ENUM_TRAITS_HPP diff --git a/backends/tests/ConnectionTest.cpp b/test/backends/ConnectionTest.cpp similarity index 63% rename from backends/tests/ConnectionTest.cpp rename to test/backends/ConnectionTest.cpp index bfc0c58..a0cf8ca 100644 --- a/backends/tests/ConnectionTest.cpp +++ b/test/backends/ConnectionTest.cpp @@ -7,13 +7,14 @@ using namespace matador::sql; TEST_CASE("Create connection test", "[connection]") { - - connection c(matador::test::connection::dns); + const connection c(matador::test::connection::dns); REQUIRE(!c.is_open()); - c.open(); + auto result = c.open(); + REQUIRE(result.is_ok()); REQUIRE(c.is_open()); - c.close(); + result = c.close(); + REQUIRE(result.is_ok()); REQUIRE(!c.is_open()); } diff --git a/test/backends/QueryBasicTest.cpp b/test/backends/QueryBasicTest.cpp new file mode 100644 index 0000000..11b29dd --- /dev/null +++ b/test/backends/QueryBasicTest.cpp @@ -0,0 +1,551 @@ +#include "catch2/catch_test_macros.hpp" +#include "catch2/matchers/catch_matchers_string.hpp" + +#include "matador/sql/column_definition.hpp" +#include "matador/sql/connection.hpp" +#include "matador/query/condition.hpp" +#include "matador/query/query.hpp" + +#include "matador/sql/schema.hpp" + +#include "matador/utils/basic_types.hpp" +#include "matador/utils/string.hpp" + +#include "models/types.hpp" + +#include "QueryFixture.hpp" + +using namespace matador::test; +using namespace matador::sql; +using namespace matador::query; + +TEST_CASE_METHOD( QueryFixture, "Insert and select basic datatypes", "[query][datatypes]" ) { + schema.attach("types"); + auto res = query::create() + .table("types", schema) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + tables_to_drop.emplace("types"); + + float float_value = 2.44557f; + double double_value = 11111.23433345; + char cval = 'c'; + short sval = (std::numeric_limits::min)(); + int ival = (std::numeric_limits::min)(); + long lval = (std::numeric_limits::min)(); + long long llval = (std::numeric_limits::max)(); + unsigned char ucval = (std::numeric_limits::max)(); + unsigned short usval = (std::numeric_limits::max)(); + unsigned int uival = (std::numeric_limits::max)(); + unsigned long ulval = (std::numeric_limits::max)(); + unsigned long long ullval = (std::numeric_limits::max)(); + if (db.type() == "sqlite" || db.type() == "postgres") { + ulval = (std::numeric_limits::max)(); + ullval = (std::numeric_limits::max)(); + } + bool bval = true; + const char *cstr("Armer schwarzer Kater"); + std::string varcharval("hallo welt"); + std::string strval = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam " + "nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, " + "sed diam voluptua. At vero eos et accusam et justo duo dolores et ea " + "rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. " + "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy " + "eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. " + "At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd " + "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."; + matador::date date_val(15, 3, 2015); + auto time_val = matador::time(2015, 3, 15, 13, 56, 23, 123); + matador::utils::blob blob_val {1,2,3,4,5,6,7,8}; + + types t { + 1, + cval, sval, ival, lval, llval, + ucval, usval, uival, ulval, ullval, + float_value, double_value, + bval, + "Armer schwarzer Kater", + strval, varcharval, + date_val, time_val, + blob_val + }; + + res = query::insert() + .into("types", matador::sql::column_generator::generate(schema, true)) + .values(t) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + auto result = query::select(schema) + .from("types") + .fetch_one(db); + REQUIRE(result.is_ok()); + REQUIRE(*result != nullptr); + + REQUIRE((*result)->id_ == 1); + REQUIRE((*result)->char_ == cval); + REQUIRE((*result)->short_ == sval); + REQUIRE((*result)->int_ == ival); + REQUIRE((*result)->long_ == lval); + REQUIRE((*result)->long64_ == llval); + REQUIRE((*result)->unsigned_char_ == ucval); + REQUIRE((*result)->unsigned_short_ == usval); + REQUIRE((*result)->unsigned_int_ == uival); + REQUIRE((*result)->unsigned_long_ == ulval); + REQUIRE((*result)->unsigned_long64_ == ullval); + REQUIRE((*result)->float_ == float_value); + REQUIRE((*result)->double_ == double_value); + REQUIRE(strcmp((*result)->cstr_, cstr) == 0); + REQUIRE((*result)->bool_ == bval); + REQUIRE((*result)->varchar_ == varcharval); + REQUIRE((*result)->string_ == strval); + REQUIRE((*result)->date_ == date_val); + REQUIRE((*result)->time_ == time_val); + REQUIRE((*result)->binary_ == blob_val); +} + +TEST_CASE_METHOD( QueryFixture, "Test quoted identifier", "[query][quotes][identifier]" ) { + using namespace matador::sql; + + auto res = query::create() + .table("quotes", { + make_column("from", 255), + make_column("to", 255) + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + tables_to_drop.emplace("quotes"); + + // check table description + std::vector column_names = { "from", "to"}; + std::vector types = {matador::data_type::type_varchar, matador::data_type::type_varchar}; + const auto columns = db.describe("quotes"); + REQUIRE(columns.is_ok()); + + for (const auto &col : *columns) { + REQUIRE(col.name() == column_names[col.index()]); + REQUIRE(col.type() == types[col.index()]); + } + + res = query::insert() + .into("quotes", {"from", "to"}) + .values({"Berlin", "London"}) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + auto row = query::select({"from", "to"}) + .from("quotes") + .fetch_one(db); + REQUIRE(row.is_ok()); + + REQUIRE(row->has_value()); + REQUIRE(row.value()->at("from").as() == "Berlin"); + REQUIRE(row.value()->at("to").as() == "London"); + + res = query::update("quotes") + .set({{"from", "Hamburg"}, {"to", "New York"}}) + .where("from"_col == "Berlin") + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + row = query::select({"from", "to"}) + .from("quotes") + .fetch_one(db); + REQUIRE(row.is_ok()); + + REQUIRE(row->has_value()); + REQUIRE(row.value()->at("from").as() == "Hamburg"); + REQUIRE(row.value()->at("to").as() == "New York"); +} + +TEST_CASE_METHOD( QueryFixture, "Test quoted column names", "[query][quotes][column]" ) { + using namespace matador::sql; + + const auto start_quote = db.dialect().token_at(matador::sql::dialect_token::START_QUOTE); + const auto end_quote = db.dialect().token_at(matador::sql::dialect_token::END_QUOTE); + + const std::string column_name = "name_with_" + start_quote + "open_close_quotes" + end_quote + "_in_backend_ctx"; + + std::vector column_names = { + "normal_name", + column_name, + "name_with_'string'_\"literal\"_quotes", + "name_with_`identifier_quotes`_in_backend_ctx", + "from" + }; + tables_to_drop.emplace("quotes"); + + for (const auto &name : column_names) { + auto res = query::create() + .table("quotes", { + make_column(name, 255), + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + + const auto columns = db.describe("quotes"); + REQUIRE(columns.is_ok()); + + for (const auto &col : *columns) { + REQUIRE(col.name() == name); + REQUIRE(col.type() == matador::data_type::type_varchar); + } + + res = query::drop() + .table("quotes") + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + } +} + +TEST_CASE_METHOD(QueryFixture, "Test quoted literals", "[query][quotes][literals]") { + using namespace matador::sql; + + auto res = query::create() + .table("escapes", { + make_column("name", 255), + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + tables_to_drop.emplace("escapes"); + + res = query::insert() + .into("escapes", {"name"}) + .values({"text"}) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + auto row = query::select({"name"}) + .from("escapes") + .fetch_one(db); + REQUIRE(row.is_ok()); + + REQUIRE(row->has_value()); + REQUIRE(row.value()->at("name").as() == "text"); + + res = query::update("escapes") + .set({{"name", "text'd"}}) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + row = query::select({"name"}) + .from("escapes") + .fetch_one(db); + REQUIRE(row.is_ok()); + + REQUIRE(row->has_value()); + REQUIRE(row.value()->at("name").as() == "text'd"); + + res = query::update("escapes") + .set({{"name", "text\nhello\tworld"}}) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + row = query::select({"name"}) + .from("escapes") + .fetch_one(db); + REQUIRE(row.is_ok()); + + REQUIRE(row->has_value()); + REQUIRE(row.value()->at("name").as() == "text\nhello\tworld"); + + res = query::update("escapes") + .set({{"name", "text \"text\""}}) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + row = query::select({"name"}) + .from("escapes") + .fetch_one(db); + REQUIRE(row.is_ok()); + + REQUIRE(row->has_value()); + REQUIRE(row.value()->at("name").as() == "text \"text\""); +} + +TEST_CASE_METHOD(QueryFixture, "Test describe table", "[query][describe][table]") { + using namespace matador::sql; + + schema.attach("types"); + const auto res = query::create() + .table("types", schema) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + tables_to_drop.emplace("types"); + + const auto columns = db.describe("types"); + REQUIRE(columns.is_ok()); + + std::vector column_names = { "id", + "val_char", "val_float", "val_double", "val_short", + "val_int", "val_long", "val_long_long", "val_unsigned_char", + "val_unsigned_short", "val_unsigned_int", "val_unsigned_long", "val_unsigned_long_long", + "val_bool", "val_cstr", "val_string", "val_varchar", "val_date", "val_time", + "val_binary"}; + const std::vector> type_check = { + [](const column_definition &cf) { return cf.is_integer(); }, + [](const column_definition &cf) { return cf.is_integer(); }, + [](const column_definition &cf) { return cf.is_floating_point(); }, + [](const column_definition &cf) { return cf.is_floating_point(); }, + [](const column_definition &cf) { return cf.is_integer(); }, + [](const column_definition &cf) { return cf.is_integer(); }, + [](const column_definition &cf) { return cf.is_integer(); }, + [](const column_definition &cf) { return cf.is_integer(); }, + [](const column_definition &cf) { return cf.is_integer(); }, + [](const column_definition &cf) { return cf.is_integer(); }, + [](const column_definition &cf) { return cf.is_integer(); }, + [](const column_definition &cf) { return cf.is_integer(); }, + [](const column_definition &cf) { return cf.is_integer(); }, + [](const column_definition &cf) { return cf.is_bool(); }, + [](const column_definition &cf) { return cf.is_varchar(); }, + [](const column_definition &cf) { return cf.is_string(); }, + [](const column_definition &cf) { return cf.is_varchar(); }, + [](const column_definition &cf) { return cf.is_date(); }, + [](const column_definition &cf) { return cf.is_time(); }, + [](const column_definition &cf) { return cf.is_blob(); } + }; + + const auto &cols = columns.value(); + for (const auto &col : cols) { + REQUIRE(col.name() == column_names[col.index()]); + REQUIRE(type_check[col.index()](col)); + } +} + +TEST_CASE_METHOD(QueryFixture, "Test unknown table", "[query][table]") { + const auto result = query::select({"name"}) + .from("person") + .fetch_all(db); + + REQUIRE(result.is_error()); +} + +namespace matador::test::temporary { +struct pk { + template + void process(Operator &op) { + matador::access::primary_key(op, "id", id); + matador::access::attribute(op, "name", name, 255); + } + + unsigned long id{}; + std::string name; +}; +} + +TEST_CASE_METHOD(QueryFixture, "Test primary key", "[query][primary key]") { + using namespace matador::test::temporary; + using namespace matador::sql; + + schema.attach("pk"); + auto res = query::create() + .table("pk", schema) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + tables_to_drop.emplace("pk"); + + pk pk1{ 7, "george" }; + + res = query::insert() + .into("pk", column_generator::generate(schema)) + .values(pk1) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + auto row = query::select(schema) + .from("pk") + .fetch_one(db); + REQUIRE(row.is_ok()); + REQUIRE(*row != nullptr); + REQUIRE(row.value()->id > 0); +} + +TEST_CASE_METHOD(QueryFixture, "Test primary key prepared", "[query][primary key][prepared]") { + using namespace matador::test::temporary; + using namespace matador::sql; + + schema.attach("pk"); + auto res = query::create() + .table("pk", schema) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + tables_to_drop.emplace("pk"); + + pk pk1{ 7, "george" }; + + auto stmt = query::insert() + .into("pk", column_generator::generate(schema)) + .values() + .prepare(db); + + res = stmt.bind(pk1) + .execute(); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + stmt = query::select(schema) + .from("pk") + .prepare(db); + + auto row = stmt.fetch_one(); + REQUIRE(row.is_ok()); + REQUIRE(*row != nullptr); + REQUIRE(row.value()->id > 0); + REQUIRE(row.value()->name == "george"); +} + +namespace matador::test::temporary { +struct appointment +{ + unsigned long id{}; + std::string name; + matador::time time_point{}; + matador::date date_point{}; + + template < class Operator > + void process(Operator &op) + { + matador::access::primary_key(op, "id", id); + matador::access::attribute(op, "name", name, 255); + matador::access::attribute(op, "time_point", time_point); + matador::access::attribute(op, "date_point", date_point); + } +}; + +} +TEST_CASE_METHOD(QueryFixture, "Test select time and date", "[query][select][time]") { + using namespace matador::test::temporary; + using namespace matador::sql; + schema.attach("appointment"); + auto res = query::create() + .table("appointment", schema) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + tables_to_drop.emplace("appointment"); + + auto dinner = appointment{ 1, "dinner" }; + auto time_str = matador::utils::to_string(dinner.time_point); + auto date_str = matador::utils::to_string(dinner.date_point); + + res = query::insert() + .into("appointment", column_generator::generate(schema)) + .values(dinner) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + auto row = query::select(schema) + .from("appointment") + .fetch_one(db); + REQUIRE(row.is_ok()); + + REQUIRE(*row != nullptr); + REQUIRE(matador::utils::to_string(row.value()->time_point) == time_str); + REQUIRE(matador::utils::to_string(row.value()->date_point) == date_str); +} + +TEST_CASE_METHOD(QueryFixture, "Test null column", "[query][select][null]") { + using namespace matador::sql; + + auto res = query::create() + .table("person", { + make_pk_column("id"), + make_column("first_name", 255, null_option::NULLABLE), + make_column("last_name", 255, null_option::NULLABLE) + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + tables_to_drop.emplace("person"); + + res = query::insert() + .into("person", {"id", "first_name"}) + .values({1, "george"}) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + res = query::insert() + .into("person", {"id", "last_name"}) + .values({2, "clooney"}) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + auto result = query::select({"id", "first_name", "last_name"}) + .from("person") + .fetch_all(db); + REQUIRE(result.is_ok()); + + std::vector expected_first_names{"george", ""}; + std::vector expected_last_names{"", "clooney"}; + size_t index{0}; + for (const auto& row : *result) { + auto first_name = row.at("first_name"); + auto last_name = row.at("last_name"); + std::cout << "first name " << first_name.value() << " last name " << last_name.value() << std::endl; + REQUIRE(first_name == expected_first_names[index]); + REQUIRE(last_name == expected_last_names[index++]); + } +} + +TEST_CASE_METHOD(QueryFixture, "Test null column prepared", "[query][select][null][prepared]") { + using namespace matador::sql; + + auto res = query::create() + .table("person", { + make_pk_column("id"), + make_column("first_name", 255, null_option::NULLABLE), + make_column("last_name", 255, null_option::NULLABLE) + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + tables_to_drop.emplace("person"); + + res = query::insert() + .into("person", {"id", "first_name"}) + .values({1, "george"}) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + res = query::insert() + .into("person", {"id", "last_name"}) + .values({2, "clooney"}) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + auto result = query::select({"id", "first_name", "last_name"}) + .from("person") + .fetch_all(db); + REQUIRE(result.is_ok()); + + std::vector expected_first_names{"george", ""}; + std::vector expected_last_names{"", "clooney"}; + size_t index{0}; + for (const auto& row : *result) { + auto first_name = row.at("first_name").as(); + auto last_name = row.at("last_name").as(); + REQUIRE(first_name == expected_first_names[index]); + REQUIRE(last_name == expected_last_names[index++]); + } +} \ No newline at end of file diff --git a/test/backends/QueryFixture.cpp b/test/backends/QueryFixture.cpp new file mode 100644 index 0000000..8cc6efe --- /dev/null +++ b/test/backends/QueryFixture.cpp @@ -0,0 +1,52 @@ +#include "QueryFixture.hpp" + +#include "matador/sql/query.hpp" + +#include "catch2/catch_test_macros.hpp" + +namespace matador::test { + +QueryFixture::QueryFixture() + : db(connection::dns) + , schema(db.dialect().default_schema_name()) +{ + db.open(); +} + +QueryFixture::~QueryFixture() { + while (!tables_to_drop.empty()) { + drop_table_if_exists(tables_to_drop.top()); + tables_to_drop.pop(); + } +} + +void QueryFixture::check_table_exists(const std::string &table_name) const +{ + auto result = db.exists(table_name); + REQUIRE(result.is_ok()); + REQUIRE(*result); +} + +void QueryFixture::check_table_not_exists(const std::string &table_name) const +{ + auto result = db.exists(table_name); + REQUIRE(result.is_ok()); + REQUIRE(!*result); +} + +void QueryFixture::drop_table_if_exists(const std::string &table_name) const { + const auto result = db.exists(table_name).and_then([&table_name, this](bool exists) { + if (exists) { + if (sql::query::drop() + .table(table_name) + .execute(db).is_ok()) { + this->check_table_not_exists(table_name); + } else { + FAIL("Failed to drop table"); + } + } + return utils::ok(true); + }); +} + +} \ No newline at end of file diff --git a/test/backends/QueryFixture.hpp b/test/backends/QueryFixture.hpp new file mode 100644 index 0000000..07c31fd --- /dev/null +++ b/test/backends/QueryFixture.hpp @@ -0,0 +1,32 @@ +#ifndef MATADOR_QUERY_FIXTURE_HPP +#define MATADOR_QUERY_FIXTURE_HPP + +#include "matador/sql/connection.hpp" +#include "matador/sql/schema.hpp" + +#include "connection.hpp" + +#include + +namespace matador::test { + +class QueryFixture { +public: + QueryFixture(); + ~QueryFixture(); + + void check_table_exists(const std::string &table_name) const; + void check_table_not_exists(const std::string &table_name) const; + +protected: + sql::connection db; + sql::schema schema; + std::stack tables_to_drop; + +private: + void drop_table_if_exists(const std::string &table_name) const; +}; + +} + +#endif //MATADOR_QUERY_FIXTURE_HPP diff --git a/test/backends/QueryRecordTest.cpp b/test/backends/QueryRecordTest.cpp new file mode 100644 index 0000000..8751c57 --- /dev/null +++ b/test/backends/QueryRecordTest.cpp @@ -0,0 +1,772 @@ +#include + +#include "matador/sql/column.hpp" +#include "matador/sql/condition.hpp" +#include "matador/sql/connection.hpp" +#include "matador/sql/query.hpp" + +#include "matador/utils/types.hpp" +#include "matador/utils/string.hpp" + +#include "QueryFixture.hpp" + +#include +#include + +using namespace matador::sql; +using namespace matador::test; + +TEST_CASE_METHOD(QueryFixture, "Test all data types for record", "[query][record][data types]") { + check_table_not_exists("types"); + + auto res = query::create() + .table("types", { + make_pk_column("id"), + make_column("val_char"), + make_column("val_short"), + make_column("val_int"), + make_column("val_long"), + make_column("val_long_long"), + make_column("val_uchar"), + make_column("val_ushort"), + make_column("val_uint"), + make_column("val_ulong"), + make_column("val_ulong_long"), + make_column("val_bool"), + make_column("val_float"), + make_column("val_double"), + make_column("val_string"), + make_column("val_varchar", 63), + make_column("val_date"), + make_column("val_time"), + make_column("val_blob"), + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 0); + + check_table_exists("types"); + tables_to_drop.emplace("types"); + + auto cols = std::vector{"id", + "val_char", "val_short", "val_int", "val_long", "val_long_long", + "val_uchar", "val_ushort", "val_uint", "val_ulong", "val_ulong_long", + "val_bool", + "val_float", "val_double", + "val_string", "val_varchar", + "val_date", "val_time", "val_blob"}; + + const auto fields = db.describe("types"); + REQUIRE(fields.is_ok()); + for (const auto &fld : *fields) { + REQUIRE(std::find(cols.begin(), cols.end(), fld.name()) != cols.end()); + } + + unsigned long id{1}; + char c{-11}; + short s{-256}; + int i{-123456}; + long l{-9876543}; + long long ll{-987654321}; + unsigned char uc{13}; + unsigned short us{1024}; + unsigned int ui{654321}; + unsigned long ul{12345678}; + unsigned long long ull{1234567890}; + bool b{true}; + float f{3.1415f}; + double d{2.71828}; + std::string str{"long text"}; + std::string varchar{"good day"}; + auto md{matador::date()}; + auto mt{matador::time::now()}; + matador::utils::blob bin{0x01,0x02,0x03,0x04}; + + res = query::insert() + .into("types", cols) + .values({id, c, s, i, l, ll, uc, us, ui, ul, ull, b, f, d, str, varchar, md, mt, bin}) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 1); + + auto row = query::select(cols) + .from("types") + .fetch_one(db); + + REQUIRE(row.is_ok()); + REQUIRE(row.value().has_value()); + + REQUIRE(id == (*row)->at("id")); + REQUIRE(c == (*row)->at("val_char")); + REQUIRE(s == (*row)->at("val_short")); + REQUIRE(i == (*row)->at("val_int")); + REQUIRE(l == (*row)->at("val_long")); + REQUIRE(ll == (*row)->at("val_long_long")); + REQUIRE(uc == (*row)->at("val_uchar")); + REQUIRE(us == (*row)->at("val_ushort")); + REQUIRE(ui == (*row)->at("val_uint")); + REQUIRE(ul == (*row)->at("val_ulong")); + REQUIRE(ull == (*row)->at("val_ulong_long")); + REQUIRE((*row)->at("val_bool")); + REQUIRE(f == (*row)->at("val_float")); + REQUIRE(d == (*row)->at("val_double")); + REQUIRE(str == (*row)->at("val_string")); + REQUIRE(varchar == (*row)->at("val_varchar")); + REQUIRE(md == (*row)->at("val_date")); + REQUIRE(mt == (*row)->at("val_time")); + REQUIRE(bin == (*row)->at("val_blob")); +} + +TEST_CASE_METHOD(QueryFixture, "Create and drop table statement", "[query][record]") +{ + check_table_not_exists("person"); + auto res = query::create() + .table("person", { + make_pk_column("id"), + make_column("name", 255), + make_column("age") + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 0); + + check_table_exists("person"); + tables_to_drop.emplace("person"); + + res = query::drop() + .table("person") + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 0); + + check_table_not_exists("person"); +} + +TEST_CASE_METHOD(QueryFixture, "Create and drop table statement with foreign key", "[query][record]") +{ + auto res = query::create() + .table("airplane", { + make_pk_column("id"), + make_column("brand", 255), + make_column("model", 255), + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 0); + + check_table_exists("airplane"); + tables_to_drop.emplace("airplane"); + + res = query::create() + .table("flight", { + make_pk_column("id"), + make_fk_column("airplane_id", "airplane", "id"), + make_column("pilot_name", 255), + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 0); + + check_table_exists("flight"); + tables_to_drop.emplace("flight"); + + res = query::drop() + .table("flight") + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 0); + + check_table_not_exists("flight"); + + res = query::drop() + .table("airplane") + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 0); + + check_table_not_exists("airplane"); +} + +TEST_CASE_METHOD(QueryFixture, "Execute insert record statement", "[query][record]") +{ + auto res = query::create() + .table("person", { + make_pk_column("id"), + make_column("name", 255), + make_column("age") + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 0); + tables_to_drop.emplace("person"); + + res = query::insert() + .into("person", {"id", "name", "age"}) + .values({7, "george", 45}) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 1); + + auto result = query::select({"id", "name", "age"}) + .from("person") + .fetch_all(db); + + REQUIRE(result.is_ok()); + for (const auto &i: *result) { + REQUIRE(i.size() == 3); + REQUIRE(i.at(0).name() == "id"); + REQUIRE(i.at(0).is_integer()); + REQUIRE(i.at(0).template as() == 7); + REQUIRE(i.at(1).name() == "name"); + REQUIRE(i.at(1).is_varchar()); + REQUIRE(i.at(1).template as() == "george"); + REQUIRE(i.at(2).name() == "age"); + REQUIRE(i.at(2).is_integer()); + REQUIRE(i.at(2).template as() == 45); + } +} + +TEST_CASE_METHOD(QueryFixture, "Execute insert record statement with foreign key", "[query][record]") +{ + auto res = query::create() + .table("airplane", { + make_pk_column("id"), + make_column("brand", 255), + make_column("model", 255), + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 0); + tables_to_drop.emplace("airplane"); + + res = query::create() + .table("flight", { + make_pk_column("id"), + make_fk_column("airplane_id", "airplane", "id"), + make_column("pilot_name", 255), + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 0); + tables_to_drop.emplace("flight"); + + std::vector> values_list{ + {1, "Airbus", "A380"}, + {2, "Boeing", "707"}, + {3, "Boeing", "747"} + }; + + for(auto &&values : values_list) { + res = query::insert() + .into("airplane", {"id", "brand", "model"}) + .values(std::move(values)) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 1); + } + + auto count = query::select({count_all()}) + .from("airplane") + .fetch_value(db); + REQUIRE(count.is_ok()); + REQUIRE(count->has_value()); + REQUIRE(*count == 3); + + res = query::insert() + .into("flight", {"id", "airplane_id", "pilot_name"}) + .values({4, 1, "George"}) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 1); +} + +TEST_CASE_METHOD(QueryFixture, "Execute update record statement", "[query][record]") +{ + auto res = query::create() + .table("person", { + make_pk_column("id"), + make_column("name", 255), + make_column("age") + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 0); + tables_to_drop.emplace("person"); + + res = query::insert() + .into("person", {"id", "name", "age"}) + .values({7, "george", 45}) + .execute(db); + + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 1); + + res = query::update("person") + .set({{"id", 7}, + {"name", "jane"}, + {"age", 35}}) + .where("id"_col == 7) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 1); + + auto result = query::select({"id", "name", "age"}) + .from("person") + .fetch_all(db); + + REQUIRE(result.is_ok()); + for (const auto &i: *result) { + REQUIRE(i.size() == 3); + REQUIRE(i.at(0).name() == "id"); + REQUIRE(i.at(0).is_integer()); + REQUIRE(i.at(0).as() == 7); + REQUIRE(i.at(1).name() == "name"); + REQUIRE(i.at(1).is_varchar()); + REQUIRE(i.at(1).as() == "jane"); + REQUIRE(i.at(2).name() == "age"); + REQUIRE(i.at(2).is_integer()); + REQUIRE(i.at(2).as() == 35); + } +} + +TEST_CASE_METHOD(QueryFixture, "Execute select statement", "[query][record]") +{ + auto res = query::create() + .table("person", { + make_pk_column("id"), + make_column("name", 255), + make_column("age") + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 0); + tables_to_drop.emplace("person"); + + std::vector> values_list{ + {1, "george", 45}, + {2, "jane", 32}, + {3, "michael", 67}, + {4, "bob", 13} + }; + + for(auto &&values : values_list) { + res = query::insert() + .into("person", {"id", "name", "age"}) + .values(std::move(values)) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 1); + } + + auto result = query::select({"id", "name", "age"}) + .from("person") + .fetch_all(db); + REQUIRE(result.is_ok()); + + std::list expected_names{"george", "jane", "michael", "bob"}; + for (const auto &p: *result) { + REQUIRE(p.at(1).str() == expected_names.front()); + expected_names.pop_front(); + } + REQUIRE(expected_names.empty()); + + auto rec = query::select({"id", "name", "age"}) + .from("person") + .fetch_one(db); + REQUIRE(rec.is_ok()); + REQUIRE(rec->has_value()); + REQUIRE((*rec)->at(1).str() == "george"); + + auto name = query::select({"name"}) + .from("person") + .fetch_value(db); + + REQUIRE(name.is_ok()); + REQUIRE(*name == "george"); +} + +TEST_CASE_METHOD(QueryFixture, "Execute select statement with order by", "[query][record]") +{ + auto res = query::create() + .table("person", { + make_pk_column("id"), + make_column("name", 255), + make_column("age") + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 0); + tables_to_drop.emplace("person"); + + std::vector> values_list{ + {1, "george", 45}, + {2, "jane", 32}, + {3, "michael", 67}, + {4, "bob", 13} + }; + + for(auto &&values : values_list) { + res = query::insert() + .into("person", {"id", "name", "age"}) + .values(std::move(values)) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 1); + } + + auto result = query::select({"id", "name", "age"}) + .from("person") + .order_by("name").asc() + .fetch_all(db); + REQUIRE(result.is_ok()); + + std::list expected_names{"bob", "george", "jane", "michael"}; + for (const auto &p: *result) { + REQUIRE(p.at(1).str() == expected_names.front()); + expected_names.pop_front(); + } + REQUIRE(expected_names.empty()); +} + +TEST_CASE_METHOD(QueryFixture, "Execute select statement with group by and order by", "[query][record]") +{ + auto res = query::create() + .table("person", { + make_pk_column("id"), + make_column("name", 255), + make_column("age") + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + tables_to_drop.emplace("person"); + + std::vector> values_list{ + {1, "george", 45}, + {2, "jane", 45}, + {3, "joe", 45}, + {4, "michael", 13}, + {5, "bob", 13}, + {6, "charlie", 67} + }; + + for(auto &&values : values_list) { + res = query::insert() + .into("person", {"id", "name", "age"}) + .values(std::move(values)) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(res.value() == 1); + } + + auto result = query::select({count("age").as("age_count"), "age"}) + .from("person") + .group_by("age") + .order_by("age_count").desc() + .fetch_all(db); + REQUIRE(result.is_ok()); + + std::list> expected_values{{3, 45}, + {2, 13}, + {1, 67}}; + for (const auto &r: *result) { + const auto age_count_val = r.at(0); + const auto age_val = r.at(1); + REQUIRE(age_count_val == expected_values.front().first); + REQUIRE(age_val == expected_values.front().second); + expected_values.pop_front(); + } +} + +TEST_CASE_METHOD(QueryFixture, "Execute delete statement", "[query][record]") +{ + auto res = query::create() + .table("person", { + make_pk_column("id"), + make_column("name", 255), + make_column("age") + }).execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + tables_to_drop.emplace("person"); + + res = query::insert() + .into("person", {"id", "name", "age"}) + .values({1, "george", 45}) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + res = query::insert() + .into("person", {"id", "name", "age"}) + .values({2, "jane", 45}) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + auto count = query::select({count_all()}) + .from("person") + .fetch_value(db); + REQUIRE(count.is_ok()); + + REQUIRE(*count == 2); + + res = query::remove() + .from("person") + .where("id"_col == 1) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + count = query::select({count_all()}) + .from("person") + .fetch_value(db); + REQUIRE(count.is_ok()); + REQUIRE(*count == 1); +} + +TEST_CASE_METHOD(QueryFixture, "Test quoted identifier record", "[query][record]") { + auto res = query::create() + .table("quotes", { + make_column("from", 255), + make_column("to", 255) + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + tables_to_drop.emplace("quotes"); + + // check table description + std::vector columns = { "from", "to"}; + std::vector types = { + matador::data_type::type_varchar, + matador::data_type::type_varchar + }; + auto fields = db.describe("quotes"); + REQUIRE(fields.is_ok()); + + for (const auto &field : *fields) { + REQUIRE(field.name() == columns[field.index()]); + REQUIRE(field.type() == types[field.index()]); + } + + res = query::insert() + .into("quotes", {"from", "to"}) + .values({"Berlin", "London"}) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + auto result = query::select({"from", "to"}) + .from("quotes") + .fetch_one(db); + REQUIRE(result.is_ok()); + + REQUIRE(result->has_value()); + REQUIRE("Berlin" == (*result)->at("from").str()); + REQUIRE("London" == (*result)->at("to").str()); + + res = query::update("quotes") + .set({{"from", "Hamburg"}, {"to", "New York"}}) + .where("from"_col == "Berlin") + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + result = query::select({"from", "to"}) + .from("quotes") + .fetch_one(db); + REQUIRE(result.is_ok()); + + REQUIRE("Hamburg" == (*result)->at("from").str()); + REQUIRE("New York" == (*result)->at("to").str()); +} + +TEST_CASE_METHOD(QueryFixture, "Test create record", "[query][record][create]") { + check_table_not_exists("person"); + auto res = query::create() + .table("person", { + make_pk_column("id"), + make_column("name", 255), + make_column("age") + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + tables_to_drop.emplace("person"); + + check_table_exists("person"); + const std::vector cols = {"id", "name", "age"}; + const auto fields = db.describe("person"); + REQUIRE(fields.is_ok()); + + for (const auto &fld : *fields) { + REQUIRE(std::find(cols.begin(), cols.end(), fld.name()) != cols.end()); + } +} + +TEST_CASE_METHOD(QueryFixture, "Test insert record", "[query][record][insert]") { + check_table_not_exists("person"); + auto res = query::create() + .table("person", { + make_pk_column("id"), + make_column("name", 255), + make_column("age") + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + tables_to_drop.emplace("person"); + + check_table_exists("person"); + + res = query::insert() + .into("person", {"id", "name", "age"}) + .values({1, "hans", 45}) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + auto row = query::select({"id", "name", "age"}) + .from("person") + .fetch_one(db); + REQUIRE(row.is_ok()); + + REQUIRE(row->has_value()); + REQUIRE((*row)->at("id").as() == 1); + REQUIRE((*row)->at("name").as() == "hans"); + REQUIRE((*row)->at("age").as() == 45); +} + +TEST_CASE_METHOD(QueryFixture, "Test update record", "[query][record][update]") { + check_table_not_exists("person"); + auto res = query::create() + .table("person", { + make_pk_column("id"), + make_column("name", 255), + make_column("age") + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + tables_to_drop.emplace("person"); + + check_table_exists("person"); + + res = query::insert() + .into("person", {"id", "name", "age"}) + .values({1, "hans", 45}) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + auto row = query::select({"id", "name", "age"}) + .from("person") + .fetch_one(db); + REQUIRE(row.is_ok()); + + REQUIRE(row->has_value()); + REQUIRE((*row)->at("id").as() == 1); + REQUIRE((*row)->at("name").as() == "hans"); + REQUIRE((*row)->at("age").as() == 45); + + res = query::update("person") + .set({{"name", "jane"}, {"age", 47}}) + .where("name"_col == "hans") + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + row = query::select({"id", "name", "age"}) + .from("person") + .fetch_one(db); + REQUIRE(row.is_ok()); + + REQUIRE(row->has_value()); + REQUIRE((*row)->at("id").as() == 1); + REQUIRE((*row)->at("name").as() == "jane"); + REQUIRE((*row)->at("age").as() == 47); +} + +TEST_CASE_METHOD(QueryFixture, "Test prepared record statement", "[query][record][prepared]") { + check_table_not_exists("person"); + auto stmt = query::create() + .table("person", { + make_pk_column("id"), + make_column("name", 255), + make_column("age") + }) + .prepare(db); + tables_to_drop.emplace("person"); + + auto res = stmt.execute(); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + check_table_exists("person"); + + const std::vector cols = {"id", "name", "age"}; + const auto fields = db.describe("person"); + REQUIRE(fields.is_ok()); + + for (const auto &fld : *fields) { + REQUIRE(std::find(cols.begin(), cols.end(), fld.name()) != cols.end()); + } +} + +TEST_CASE_METHOD(QueryFixture, "Test scalar result", "[query][record][scalar][result]") { + check_table_not_exists("person"); + auto res = query::create() + .table("person", { + make_pk_column("id"), + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + + check_table_exists("person"); + tables_to_drop.emplace("person"); + + std::vector ids({ 1,2,3,4 }); + + for(auto id : ids) { + res = query::insert() + .into("person", {"id"}) + .values({id}) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + } + + auto stmt = query::select({"id"}) + .from("person") + .order_by("id"_col).asc() + .prepare(db); + + auto rows = stmt.fetch(); + REQUIRE(rows.is_ok()); + + size_t index{0}; + for (const auto &row : *rows) { + REQUIRE(row.at("id").as() == ids[index]); + ++index; + } + REQUIRE(index == 4); + + stmt.reset(); + + rows = stmt.fetch(); + REQUIRE(rows.is_ok()); + + index = 0; + for (const auto &row : *rows) { + REQUIRE(row.at("id").as() == ids[index]); + ++index; + } + REQUIRE(index == 4); + + stmt.reset(); + + auto row = stmt.fetch_one(); + REQUIRE(row.is_ok()); + REQUIRE(row->has_value()); + REQUIRE(row.value()->at("id").as() == ids[0]); +} \ No newline at end of file diff --git a/test/backends/QueryStatementTests.cpp b/test/backends/QueryStatementTests.cpp new file mode 100644 index 0000000..fe384a5 --- /dev/null +++ b/test/backends/QueryStatementTests.cpp @@ -0,0 +1,293 @@ +#include + +#include "matador/sql/column.hpp" +#include "matador/sql/condition.hpp" +#include "matador/sql/connection.hpp" +#include "matador/sql/query.hpp" + +#include "models/person.hpp" + +#include "QueryFixture.hpp" + +#include +#include + +using namespace matador::sql; +using namespace matador::test; + +TEST_CASE_METHOD(QueryFixture, "Test create statement", "[query][statement][create]") { + schema.attach("person"); + auto stmt = query::create() + .table("person", schema) + .prepare(db); + + auto res = stmt.execute(); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + tables_to_drop.emplace("person"); + + check_table_exists("person"); + const std::vector cols = {"id", "name", "age", "image"}; + const auto fields = db.describe("person"); + REQUIRE(fields.is_ok()); + + for (const auto &fld : *fields) { + REQUIRE(std::find(cols.begin(), cols.end(), fld.name()) != cols.end()); + } +} + +TEST_CASE_METHOD(QueryFixture, "Test insert statement", "[query][statement][insert]") { + using namespace matador::test; + + schema.attach("person"); + auto stmt = query::create() + .table("person", schema) + .prepare(db); + + auto res = stmt.execute(); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + tables_to_drop.emplace("person"); + + check_table_exists("person"); + + person george{1, "george", 45, {1,2,3,4}}; + + stmt = query::insert() + .into("person", schema) + .values() + .prepare(db); + + res = stmt.bind(george) + .execute(); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + auto row = query::select(schema) + .from("person") + .fetch_one(db); + REQUIRE(row.is_ok()); + + REQUIRE(*row != nullptr); + REQUIRE((*row)->id == 1); + REQUIRE((*row)->name == "george"); + REQUIRE((*row)->age == 45); + REQUIRE((*row)->image == matador::utils::blob{1,2,3,4}); +} + +TEST_CASE_METHOD(QueryFixture, "Test update statement", "[query][statement][update]") { + using namespace matador::test; + using namespace matador::utils; + + schema.attach("person"); + auto stmt = query::create() + .table("person", schema) + .prepare(db); + + auto res = stmt.execute(); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + tables_to_drop.emplace("person"); + + check_table_exists("person"); + + person george{1, "george", 45, {1,2,3,4}}; + + stmt = query::insert() + .into("person", schema) + .values() + .prepare(db); + + res = stmt.bind(george) + .execute(); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + auto row = query::select(schema) + .from("person") + .fetch_one(db); + REQUIRE(row.is_ok()); + + REQUIRE(*row != nullptr); + REQUIRE((*row)->id == 1); + REQUIRE((*row)->name == "george"); + REQUIRE((*row)->age == 45); + REQUIRE((*row)->image == blob{1,2,3,4}); + + george.age = 36; + george.image = {5,6,7,8}; + stmt = query::update("person") + .set() + .where("id"_col == _) + .prepare(db); + + res = stmt.bind(george) + .bind(4, george.id) + .execute(); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + row = query::select(schema) + .from("person") + .fetch_one(db); + REQUIRE(row.is_ok()); + + REQUIRE(*row != nullptr); + REQUIRE((*row)->id == 1); + REQUIRE((*row)->name == "george"); + REQUIRE((*row)->age == 36); + REQUIRE((*row)->image == blob{5,6,7,8}); +} + +TEST_CASE_METHOD(QueryFixture, "Test delete statement", "[query][statement][delete]") { + using namespace matador::test; + + schema.attach("person"); + auto stmt = query::create() + .table("person", schema) + .prepare(db); + + auto res = stmt.execute(); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + tables_to_drop.emplace("person"); + + check_table_exists("person"); + + stmt = query::insert() + .into("person", schema) + .values() + .prepare(db); + + std::vector peoples { + {1,"george", 45, {1,2,3,4}}, + {2,"jane", 36, {1,2,3,4}}, + {3,"lukas", 68, {1,2,3,4}}, + {4,"merlin", 99, {1,2,3,4}} + }; + + for (const auto &p : peoples) { + res = stmt.bind(p) + .execute(); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + stmt.reset(); + } + + auto select_stmt = query::select(schema) + .from("person") + .where("name"_col == matador::utils::_) + .prepare(db); + + auto rows = select_stmt + .bind(0, "jane") + .fetch(); + REQUIRE(rows.is_ok()); + + for (const auto &r : *rows) { + constexpr size_t index = 1; + REQUIRE(r.id == peoples[index].id); + REQUIRE(r.name == peoples[index].name); + REQUIRE(r.age == peoples[index].age); + REQUIRE(r.image == peoples[index].image); + } + + stmt = query::remove() + .from("person") + .where("name"_col == matador::utils::_) + .prepare(db); + + res = stmt.bind(0, "jane") + .execute(); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + select_stmt.reset(); + auto row = select_stmt + .bind(0, "jane") + .fetch_one(); + REQUIRE(row.is_ok()); + + REQUIRE(*row == nullptr); + + stmt.reset(); + res = stmt + .bind(0, "merlin") + .execute(); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + select_stmt.reset(); + row = select_stmt + .bind(0, "merlin") + .fetch_one(); + REQUIRE(row.is_ok()); + + REQUIRE(*row == nullptr); +} + +TEST_CASE_METHOD(QueryFixture, "Test reuse prepared statement", "[query][statement][reuse]") { + using namespace matador::test; + + schema.attach("person"); + auto stmt = query::create() + .table("person", schema) + .prepare(db); + + auto res = stmt.execute(); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + tables_to_drop.emplace("person"); + + check_table_exists("person"); + + stmt = query::insert() + .into("person", schema) + .values() + .prepare(db); + + std::vector peoples { + {1,"george", 45, {1,2,3,4}}, + {2,"jane", 36, {1,2,3,4}}, + {3,"lukas", 68, {1,2,3,4}}, + {4,"merlin", 99, {1,2,3,4}} + }; + + for (const auto &p : peoples) { + res = stmt.bind(p) + .execute(); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + stmt.reset(); + } + + stmt = query::select(schema) + .from("person") + .prepare(db); + + auto rows = stmt.fetch(); + REQUIRE(rows.is_ok()); + + size_t index = 0; + for (const auto &r : *rows) { + REQUIRE(r.id == peoples[index].id); + REQUIRE(r.name == peoples[index].name); + REQUIRE(r.age == peoples[index].age); + REQUIRE(r.image == peoples[index].image); + ++index; + } + + stmt.reset(); + + rows = stmt.fetch(); + REQUIRE(rows.is_ok()); + + index = 0; + for (const auto &r : *rows) { + REQUIRE(r.id == peoples[index].id); + REQUIRE(r.name == peoples[index].name); + REQUIRE(r.age == peoples[index].age); + REQUIRE(r.image == peoples[index].image); + ++index; + } +} \ No newline at end of file diff --git a/test/backends/QueryTest.cpp b/test/backends/QueryTest.cpp new file mode 100644 index 0000000..9d0fcd2 --- /dev/null +++ b/test/backends/QueryTest.cpp @@ -0,0 +1,532 @@ +#include "catch2/catch_test_macros.hpp" + +#include "matador/sql/column_definition.hpp" +#include "matador/sql/condition.hpp" +#include "matador/sql/query.hpp" + +#include "QueryFixture.hpp" + +#include "models/airplane.hpp" +#include "models/flight.hpp" +#include "models/person.hpp" +#include "models/recipe.hpp" + +using namespace matador::sql; +using namespace matador::test; + +TEST_CASE_METHOD(QueryFixture, "Create table with foreign key relation", "[query][foreign][relation]") +{ + schema.attach("airplane"); + schema.attach("flight"); + auto res = query::create() + .table("airplane", schema) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + + check_table_exists("airplane"); + tables_to_drop.emplace("airplane"); + + res = query::create() + .table("flight", schema) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + + check_table_exists("flight"); + tables_to_drop.emplace("flight"); +} + +TEST_CASE_METHOD(QueryFixture, "Execute select statement with where clause", "[query][where]") +{ + schema.attach("person"); + auto res = query::create() + .table("person", schema) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + + check_table_exists("person"); + tables_to_drop.emplace("person"); + + person george{7, "george", 45}; + george.image.emplace_back(37); + + res = query::insert() + .into("person", column_generator::generate(schema, true)) + .values(george) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + // fetch person as record + auto result_record = query::select(column_generator::generate(schema, true)) + .from("person") + .where("id"_col == 7) + .fetch_all(db); + REQUIRE(result_record.is_ok()); + + for (const auto &i: *result_record) { + REQUIRE(i.size() == 4); + REQUIRE(i.at(0).name() == "id"); + REQUIRE(i.at(0).is_integer()); + REQUIRE(i.at(0).as() == george.id); + REQUIRE(i.at(1).name() == "name"); + REQUIRE(i.at(1).is_varchar()); + REQUIRE(i.at(1).as() == george.name); + REQUIRE(i.at(2).name() == "age"); + REQUIRE(i.at(2).is_integer()); + REQUIRE(i.at(2).as() == george.age); + } + + // fetch person as person + auto result_person = query::select(column_generator::generate(schema, true)) + .from("person") + .where("id"_col == 7) + .fetch_all(db); + REQUIRE(result_person.is_ok()); + + for (const auto &i: *result_person) { + REQUIRE(i.id == 7); + REQUIRE(i.name == "george"); + REQUIRE(i.age == 45); + } +} + +TEST_CASE_METHOD(QueryFixture, "Execute insert statement", "[query][insert]") +{ + auto res = query::create() + .table("person", { + make_pk_column("id"), + make_column("name", 255), + make_column("color", 63) + }) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + + REQUIRE(db.exists("person")); + tables_to_drop.emplace("person"); + + res = query::insert() + .into("person", {{"", "id", ""}, {"", "name", ""}, {"", "color", ""}}) + .values({7, "george", "green"}) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + // fetch person as record + auto result_record = query::select({"id", "name", "color"}) + .from("person") + .where("id"_col == 7) + .fetch_all(db); + REQUIRE(result_record.is_ok()); + + for (const auto &i: *result_record) { + REQUIRE(i.size() == 3); + REQUIRE(i.at(0).name() == "id"); + REQUIRE(i.at(0).is_integer()); + REQUIRE(i.at(0).as() == 7); + REQUIRE(i.at(1).name() == "name"); + REQUIRE(i.at(1).is_varchar()); + REQUIRE(i.at(1).as() == "george"); + REQUIRE(i.at(2).name() == "color"); + REQUIRE(i.at(2).is_varchar()); + REQUIRE(i.at(2).as() == "green"); + } +} + +TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key", "[query][foreign]") +{ + schema.attach("airplane"); + schema.attach("flight"); + auto res = query::create() + .table("airplane", schema) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + + REQUIRE(db.exists("airplane")); + tables_to_drop.emplace("airplane"); + + res = query::create() + .table("flight", schema) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + + REQUIRE(db.exists("flight")); + tables_to_drop.emplace("flight"); + + std::vector> planes{ + matador::object_ptr(new airplane{1, "Airbus", "A380"}), + matador::object_ptr(new airplane{2, "Boeing", "707"}), + matador::object_ptr(new airplane{3, "Boeing", "747"}) + }; + + for (const auto &plane: planes) { + res = query::insert() + .into("airplane", column_generator::generate(schema, true)) + .values(*plane) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + } + + auto count = query::select({count_all()}) + .from("airplane") + .fetch_value(db); + REQUIRE(count.is_ok()); + REQUIRE(*count == 3); + + flight f4711{4, planes.at(1), "hans"}; + + res = query::insert() + .into("flight", column_generator::generate(schema, true)) + .values(f4711) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + auto f = query::select(column_generator::generate(schema, true)) + .from("flight") + .fetch_one(db); + REQUIRE(f.is_ok()); + + REQUIRE(f.value()->at(0).as() == 4); + REQUIRE(f.value()->at(1).as() == 2); + REQUIRE(f.value()->at(2).as() == "hans"); +} + +TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and join_left", "[query][foreign][join_left]") +{ + schema.attach("airplane"); + schema.attach("flight"); + auto res = query::create() + .table("airplane", schema) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + + REQUIRE(db.exists("airplane")); + tables_to_drop.emplace("airplane"); + + res = query::create() + .table("flight", schema) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + + REQUIRE(db.exists("flight")); + tables_to_drop.emplace("flight"); + + std::vector> planes{ + matador::object_ptr(new airplane{1, "Airbus", "A380"}), + matador::object_ptr(new airplane{2, "Boeing", "707"}), + matador::object_ptr(new airplane{3, "Boeing", "747"}) + }; + + for (const auto &plane: planes) { + res = query::insert() + .into("airplane", column_generator::generate(schema, true)) + .values(*plane) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + } + + auto count = query::select({count_all()}) + .from("airplane") + .fetch_value(db).value(); + REQUIRE(count == 3); + + std::vector> flights{ + matador::object_ptr(new flight{4, planes.at(0), "hans"}), + matador::object_ptr(new flight{5, planes.at(0), "otto"}), + matador::object_ptr(new flight{6, planes.at(1), "george"}), + matador::object_ptr(new flight{7, planes.at(2), "paul"}) + }; + + for (const auto &f: flights) { + res = query::insert() + .into("flight", {"id", "airplane_id", "pilot_name"}) + .values(*f) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + } + + auto f = query::select(column_generator::generate(schema, true)) + .from("flight") + .fetch_one(db); + REQUIRE(f.is_ok()); + REQUIRE(f->has_value()); + REQUIRE(f.value()->at(0).as() == 4); + REQUIRE(f.value()->at(1).as() == 1); + REQUIRE(f.value()->at(2).as() == "hans"); + + auto result = query::select({"f.id", "ap.brand", "ap.model", "f.pilot_name"}) + .from({"flight", "f"}) + .join_left({"airplane", "ap"}) + .on("f.airplane_id"_col == "ap.id"_col) + .order_by("f.id").asc() + .fetch_all(db); + REQUIRE(result.is_ok()); + + std::vector> expected_result { + {4, "hans"}, + {5, "otto"}, + {6, "george"}, + {7, "paul"} + }; + size_t index{0}; + for (const auto &r: *result) { + REQUIRE(r.size() == 4); + REQUIRE(r.at(0).as() == expected_result[index].first); + REQUIRE(r.at(3).as() == expected_result[index++].second); + } +} + +TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single entity", "[query][join_left][find]") { + schema.attach("airplane"); + schema.attach("flight"); + auto res = query::create() + .table("airplane", schema) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + + REQUIRE(db.exists("airplane")); + tables_to_drop.emplace("airplane"); + + res = query::create() + .table("flight", schema) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + + REQUIRE(db.exists("flight")); + tables_to_drop.emplace("flight"); + + std::vector> planes{ + matador::object_ptr(new airplane{1, "Airbus", "A380"}), + matador::object_ptr(new airplane{2, "Boeing", "707"}), + matador::object_ptr(new airplane{3, "Boeing", "747"}) + }; + + for (const auto &plane: planes) { + res = query::insert() + .into("airplane", column_generator::generate(schema, true)) + .values(*plane) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + } + + auto count = query::select({count_all()}) + .from("airplane") + .fetch_value(db); + REQUIRE(count.is_ok()); + REQUIRE(count->has_value()); + REQUIRE(count->value() == 3); + + std::vector> flights{ + matador::object_ptr(new flight{4, planes.at(0), "hans"}), + matador::object_ptr(new flight{5, planes.at(0), "otto"}), + matador::object_ptr(new flight{6, planes.at(1), "george"}), + matador::object_ptr(new flight{7, planes.at(2), "paul"}) + }; + + for (const auto &f: flights) { + res = query::insert() + .into("flight", column_generator::generate(schema, true)) + .values(*f) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + } + + auto f = query::select(column_generator::generate(schema, true)) + .from("flight") + .fetch_one(db); + REQUIRE(f.is_ok()); + REQUIRE(f->has_value()); + REQUIRE(f.value()->at(0).as() == 4); + REQUIRE(f.value()->at(1).as() == 1); + REQUIRE(f.value()->at(2).as() == "hans"); + + auto result = query::select({"f.id", "f.airplane_id", "ap.brand", "ap.model", "f.pilot_name"}) + .from({"flight", "f"}) + .join_left({"airplane", "ap"}) + .on("f.airplane_id"_col == "ap.id"_col) + .where("f.id"_col == 4) + .fetch_one(db); + + auto expected_flight = flights[0]; + + REQUIRE(result.is_ok()); + REQUIRE(*result); + REQUIRE(result.value()->id == expected_flight->id); + REQUIRE(result.value()->pilot_name == expected_flight->pilot_name); + REQUIRE(result.value()->airplane.get()); + REQUIRE(result.value()->airplane->id == 1); + REQUIRE(result.value()->airplane->model == "A380"); + REQUIRE(result.value()->airplane->brand == "Airbus"); +} + +TEST_CASE_METHOD(QueryFixture, "Select statement with many to many relationship", "[query][join][many_to_many]") { + schema.attach("recipes"); + schema.attach("ingredients"); + schema.attach("recipe_ingredients"); + auto res = query::create() + .table("recipes", schema) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + + REQUIRE(db.exists("recipes")); + tables_to_drop.emplace("recipes"); + + res = query::create() + .table("ingredients", schema) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + + REQUIRE(db.exists("ingredients")); + tables_to_drop.emplace("ingredients"); + + res = query::create() + .table("recipe_ingredients", schema) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 0); + + REQUIRE(db.exists("recipe_ingredients")); + tables_to_drop.emplace("recipe_ingredients"); + + std::vector ingredients { + {1, "Apple"}, + {2, "Strawberry"}, + {3, "Pineapple"}, + {4, "Sugar"}, + {5, "Flour"}, + {6, "Butter"}, + {7, "Beans"} + }; + + for (const auto &i: ingredients) { + res = query::insert() + .into("ingredients", column_generator::generate(schema, true)) + .values(i) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + } + + std::vector recipes{ + {7, "Apple Crumble"}, + {8, "Beans Chili"}, + {9, "Fruit Salad"} + }; + + for (const auto &r: recipes) { + res = query::insert() + .into("recipes", column_generator::generate(schema, true)) + .values(r) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + } + + std::vector> recipe_ingredients { + { 7, 1 }, + { 7, 4 }, + { 7, 5 }, + { 8, 6 }, + { 8, 7 }, + { 9, 1 }, + { 9, 2 }, + { 9, 3 } + }; + + for (const auto &ri: recipe_ingredients) { + res = query::insert() + .into("recipe_ingredients", column_generator::generate(schema, true)) + .values({ri.first, ri.second}) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + } + + auto result = query::select({"r.id", "r.name", "ri.ingredient_id"}) + .from({"recipes", "r"}) + .join_left({"recipe_ingredients", "ri"}) + .on("r.id"_col == "ri.recipe_id"_col) + .fetch_all(db); + REQUIRE(result.is_ok()); + + std::vector> expected_result_one_join { + {7, "Apple Crumble", 1}, + {7, "Apple Crumble", 4}, + {7, "Apple Crumble", 5}, + {8, "Beans Chili", 6}, + {8, "Beans Chili", 7}, + {9, "Fruit Salad", 1}, + {9, "Fruit Salad", 2}, + {9, "Fruit Salad", 3} + }; + size_t index{0}; + for (const auto &r: *result) { + REQUIRE(r.size() == 3); + REQUIRE(r.at(0).as().value() == std::get<0>(expected_result_one_join[index])); + REQUIRE(r.at(1).as().value() == std::get<1>(expected_result_one_join[index])); + REQUIRE(r.at(2).as().value() == std::get<2>(expected_result_one_join[index])); + ++index; + } + + result = query::select({"r.id", "r.name", "ri.ingredient_id", "i.name"}) + .from({"recipes", "r"}) + .join_left({"recipe_ingredients", "ri"}).on("r.id"_col == "ri.recipe_id"_col) + .join_left({"ingredients", "i"}).on("ri.ingredient_id"_col == "i.id"_col) + .fetch_all(db); + REQUIRE(result.is_ok()); + + std::vector> expected_result_two_joins { + {7, "Apple Crumble", 1, "Apple"}, + {7, "Apple Crumble", 4, "Sugar"}, + {7, "Apple Crumble", 5, "Flour"}, + {8, "Beans Chili", 6, "Butter"}, + {8, "Beans Chili", 7, "Beans"}, + {9, "Fruit Salad", 1, "Apple"}, + {9, "Fruit Salad", 2, "Strawberry"}, + {9, "Fruit Salad", 3, "Pineapple"} + }; + index = 0; + for (const auto &r: *result) { + REQUIRE(r.size() == 4); + REQUIRE(r.at(0).as().value() == std::get<0>(expected_result_two_joins[index])); + REQUIRE(r.at(1).as().value() == std::get<1>(expected_result_two_joins[index])); + REQUIRE(r.at(2).as().value() == std::get<2>(expected_result_two_joins[index])); + REQUIRE(r.at(3).as().value() == std::get<3>(expected_result_two_joins[index])); + ++index; + } + + result = query::select({"r.id", "r.name", "ri.ingredient_id", "i.name"}) + .from({"recipes", "r"}) + .join_left({"recipe_ingredients", "ri"}).on("r.id"_col == "ri.recipe_id"_col) + .join_left({"ingredients", "i"}).on("ri.ingredient_id"_col == "i.id"_col) + .where("r.id"_col == 8) + .fetch_all(db); + REQUIRE(result.is_ok()); + + index = 3; + for (const auto &r: *result) { + REQUIRE(r.size() == 4); + REQUIRE(r.at(0).as().value() == std::get<0>(expected_result_two_joins[index])); + REQUIRE(r.at(1).as().value() == std::get<1>(expected_result_two_joins[index])); + REQUIRE(r.at(2).as().value() == std::get<2>(expected_result_two_joins[index])); + REQUIRE(r.at(3).as().value() == std::get<3>(expected_result_two_joins[index])); + ++index; + } +} \ No newline at end of file diff --git a/test/backends/SessionFixture.cpp b/test/backends/SessionFixture.cpp new file mode 100644 index 0000000..95e4b39 --- /dev/null +++ b/test/backends/SessionFixture.cpp @@ -0,0 +1,21 @@ +#include "SessionFixture.hpp" + +namespace matador::test { + +SessionFixture::SessionFixture() + : pool(connection::dns, 4), ses(pool) {} + +SessionFixture::~SessionFixture() { + while (!tables_to_drop.empty()) { + drop_table_if_exists(tables_to_drop.top()); + tables_to_drop.pop(); + } +} + +void SessionFixture::drop_table_if_exists(const std::string &table_name) const { + if (ses.table_exists(table_name)) { + ses.drop_table(table_name); + } +} + +} \ No newline at end of file diff --git a/test/backends/SessionFixture.hpp b/test/backends/SessionFixture.hpp new file mode 100644 index 0000000..31fd6ed --- /dev/null +++ b/test/backends/SessionFixture.hpp @@ -0,0 +1,29 @@ +#ifndef MATADOR_SESSION_FIXTURE_HPP +#define MATADOR_SESSION_FIXTURE_HPP + +#include "matador/sql/session.hpp" + +#include "connection.hpp" + +#include + +namespace matador::test { + +class SessionFixture { +public: + SessionFixture(); + ~SessionFixture(); + +protected: + sql::connection_pool pool; + sql::session ses; + std::stack tables_to_drop; + +private: + void drop_table_if_exists(const std::string &table_name) const; + +}; + +} + +#endif //MATADOR_SESSION_FIXTURE_HPP diff --git a/test/backends/SessionTest.cpp b/test/backends/SessionTest.cpp new file mode 100644 index 0000000..f6f33eb --- /dev/null +++ b/test/backends/SessionTest.cpp @@ -0,0 +1,136 @@ +#include "catch2/catch_test_macros.hpp" + +#include "SessionFixture.hpp" + +#include "models/airplane.hpp" +#include "models/author.hpp" +#include "models/book.hpp" +#include "models/flight.hpp" + +using namespace matador; +using namespace matador::test; + +TEST_CASE_METHOD(SessionFixture, "Session relation test", "[session][relation]") { + ses.attach("airplanes"); + ses.attach("flights"); + ses.create_schema(); + + tables_to_drop.emplace("airplanes"); + tables_to_drop.emplace("flights"); + + auto plane = ses.insert(1, "Boeing", "A380"); + auto f = ses.insert(2, plane, "sully"); + + const auto result = ses.find(2); + REQUIRE(result.is_ok()); + REQUIRE(result->get()->id == f->id); + REQUIRE(result->get()->pilot_name == f->pilot_name); + REQUIRE(result->get()->airplane); + REQUIRE(result->get()->airplane->id == plane->id); + REQUIRE(result->get()->airplane->brand == plane->brand); + REQUIRE(result->get()->airplane->model == plane->model); +} + +TEST_CASE_METHOD(SessionFixture, "Use session to find object with id", "[session][find]") { + ses.attach("airplanes"); + ses.create_schema(); + + tables_to_drop.emplace("airplanes"); + + auto a380 = ses.insert(1, "Boeing", "A380"); + + auto result = ses.find(2); + REQUIRE(!result.is_ok()); + REQUIRE((result.err().ec() == sql::session_error_code::FailedToFindObject)); + + result = ses.find(1); + + REQUIRE(result); + auto read_a380 = result.value(); + REQUIRE(a380->id == read_a380->id); +} + +TEST_CASE_METHOD(SessionFixture, "Use session to find all objects", "[session][find]") { + ses.attach("airplanes"); + ses.create_schema(); + + tables_to_drop.emplace("airplanes"); + + std::vector> planes; + planes.emplace_back(new airplane(1, "Airbus", "A380")); + planes.emplace_back(new airplane(2, "Boeing", "707")); + planes.emplace_back(new airplane(3, "Boeing", "747")); + + for (auto &&plane: planes) { + ses.insert(plane.release()); + } + + auto result = ses.find(); + + std::vector> expected_result { + {1, "Airbus", "A380"}, + {2, "Boeing", "707"}, + {3, "Boeing", "747"} + }; + REQUIRE(result); + auto all_planes = result.release(); + size_t index {0}; + for (const auto &i: all_planes) { + REQUIRE(i.id == std::get<0>(expected_result[index])); + REQUIRE(i.brand == std::get<1>(expected_result[index])); + REQUIRE(i.model == std::get<2>(expected_result[index])); + ++index; + } +} + +TEST_CASE_METHOD(SessionFixture, "Use session to find all objects with one-to-many relation", "[session][find][one-to-many]") { + ses.attach("authors"); + ses.attach("books"); + + tables_to_drop.emplace("authors"); + tables_to_drop.emplace("books"); + + ses.create_schema(); + + std::vector> authors; + authors.emplace_back(new author{1, "Michael", "Crichton", "23.10.1942", 1975, true, {}}); + authors.emplace_back(new author{ 2, "Steven", "King", "21.9.1947", 1956, false, {}}); + + for (auto &&a: authors) { + ses.insert(a.release()); + } + + auto result = ses.find(); + REQUIRE(result.is_ok()); + auto all_authors = result.release(); + std::vector> author_repo; + for (auto it = all_authors.begin(); it != all_authors.end(); ++it) { + std::cout << "author: " << it->first_name << " (books: " << it->books.size() << ")\n"; + author_repo.emplace_back(it.release()); + } + REQUIRE(author_repo.size() == 2); + + std::vector> books; + books.emplace_back( new book{3, "Jurassic Park", author_repo[0], 1990} ); + books.emplace_back( new book{4, "Timeline", author_repo[0], 1999} ); + books.emplace_back( new book{5, "The Andromeda Strain", author_repo[0], 1969} ); + books.emplace_back( new book{6, "Congo", author_repo[0], 1980} ); + books.emplace_back( new book{7, "Prey", author_repo[0], 2002} ); + books.emplace_back( new book{8, "Carrie", author_repo[1], 1974} ); + books.emplace_back( new book{9, "The Shining", author_repo[1], 1977} ); + books.emplace_back( new book{10, "It", author_repo[1], 1986} ); + books.emplace_back( new book{11, "Misery", author_repo[1], 1987} ); + books.emplace_back( new book{12, "The Dark Tower: The Gunslinger", author_repo[1], 1982} ); + + for (auto &&b: books) { + ses.insert(b.release()); + } + + result = ses.find(); + REQUIRE(result); + + all_authors = result.release(); + for (auto it = all_authors.begin(); it != all_authors.end(); ++it) { + std::cout << "author: " << it->first_name << " (books: " << it->books.size() << ")\n"; + } +} \ No newline at end of file diff --git a/backends/tests/StatementCacheTest.cpp b/test/backends/StatementCacheTest.cpp similarity index 79% rename from backends/tests/StatementCacheTest.cpp rename to test/backends/StatementCacheTest.cpp index 786b68a..0e2124e 100644 --- a/backends/tests/StatementCacheTest.cpp +++ b/test/backends/StatementCacheTest.cpp @@ -6,6 +6,7 @@ #include "matador/sql/statement_cache.hpp" #include "connection.hpp" +#include "matador/sql/dialect_builder.hpp" using namespace matador; @@ -23,9 +24,10 @@ protected: }; TEST_CASE_METHOD(StatementCacheFixture, "Acquire prepared statement", "[statement cache]") { - sql::statement_cache cache; + // const auto d = sql::dialect_builder::builder().create().build(); + // sql::statement_cache cache(pool, d); - auto conn = pool.acquire(); + // auto conn = pool.acquire(); std::string sql = R"(SELECT * FROM person WHERE name = 'george')"; // auto &stmt = cache.acquire(sql, *conn); diff --git a/test/backends/StatementTest.cpp b/test/backends/StatementTest.cpp new file mode 100644 index 0000000..cb64f23 --- /dev/null +++ b/test/backends/StatementTest.cpp @@ -0,0 +1,114 @@ +#include + +#include "matador/sql/column_definition.hpp" +#include "matador/sql/condition.hpp" +#include "matador/sql/connection.hpp" +#include "matador/sql/query.hpp" + +#include "matador/object/object_ptr.hpp" + +#include "QueryFixture.hpp" + +#include "models/airplane.hpp" + +using namespace matador::sql; +using namespace matador::test; + +namespace matador::test::detail { +template +[[maybe_unused]] object_ptr make_object_ptr(Args&&... args) +{ + return object_ptr(new Type(std::forward(args)...)); +} +} + +class StatementTestFixture : public QueryFixture +{ +public: + StatementTestFixture() + { + const auto res = query::create() + .table("airplane", schema) + .execute(db); + REQUIRE(res.is_ok()); + tables_to_drop.emplace("airplane"); + } + +protected: + std::vector> planes{ + matador::test::detail::make_object_ptr(1, "Airbus", "A380"), + matador::test::detail::make_object_ptr(2, "Boeing", "707"), + matador::test::detail::make_object_ptr(3, "Boeing", "747") + }; +}; + +TEST_CASE_METHOD(StatementTestFixture, "Create prepared statement", "[statement]") +{ + using namespace matador::utils; + schema.attach("airplane"); + table ap{"airplane"}; + SECTION("Insert with prepared statement and placeholder") { + auto stmt = query::insert() + .into("airplane", column_generator::generate(schema, true)) + .values() + .prepare(db); + + for (const auto &plane: planes) { + auto res = stmt.bind(*plane).execute(); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + stmt.reset(); + } + + auto result = query::select(column_generator::generate(schema, true)) + .from(ap) + .fetch_all(db); + + REQUIRE(result.is_ok()); + size_t index{0}; + for (const auto &i: *result) { + REQUIRE(i.id == planes[index]->id); + REQUIRE(i.brand == planes[index]->brand); + REQUIRE(i.model == planes[index++]->model); + } + } + + SECTION("Select with prepared statement") { + for (const auto &plane: planes) { + auto res = query::insert() + .into("airplane", column_generator::generate(schema, true)) + .values(*plane) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + } + + auto stmt = query::select(column_generator::generate(schema, true)) + .from(ap) + .where("brand"_col == _) + .prepare(db); + + auto result = stmt.bind(0, "Airbus") + .fetch(); + + REQUIRE(result.is_ok()); + for (const auto &i: *result) { + REQUIRE(i.id == planes[0]->id); + REQUIRE(i.brand == planes[0]->brand); + REQUIRE(i.model == planes[0]->model); + } + + stmt.reset(); + + result = stmt.bind(0, "Boeing") + .fetch(); + + size_t index{1}; + REQUIRE(result.is_ok()); + for (const auto &i: *result) { + REQUIRE(i.id == planes[index]->id); + REQUIRE(i.brand == planes[index]->brand); + REQUIRE(i.model == planes[index++]->model); + } + } +} \ No newline at end of file diff --git a/test/backends/TypeTraitsTest.cpp b/test/backends/TypeTraitsTest.cpp new file mode 100644 index 0000000..7ab1a2f --- /dev/null +++ b/test/backends/TypeTraitsTest.cpp @@ -0,0 +1,79 @@ +#include + +#include "ColorEnumTraits.hpp" + +#include "matador/sql/connection.hpp" +#include "matador/sql/column_generator.hpp" +#include "matador/sql/query.hpp" + +#include "QueryFixture.hpp" + +#include "models/location.hpp" + +using namespace matador::sql; +using namespace matador::test; + +class TypeTraitsTestFixture : public QueryFixture +{ +public: + TypeTraitsTestFixture() + { + db.open(); + const auto res = query::create() + .table("location", schema) + .execute(db); + tables_to_drop.emplace("location"); + } +}; + +TEST_CASE_METHOD(TypeTraitsTestFixture, "Special handling of attributes with type traits", "[typetraits]") +{ + schema.attach("location"); + SECTION("Insert and select with direct execution") { + location loc{1, "center", {1, 2, 3}, Color::Black}; + + auto res = query::insert() + .into("location", column_generator::generate(schema, true)) + .values(loc) + .execute(db); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + auto result = query::select(column_generator::generate(schema, true)) + .from("location") + .fetch_one(db); + + REQUIRE(result.is_ok()); + REQUIRE((*result)->name == "center"); + REQUIRE((*result)->color == Color::Black); + REQUIRE((*result)->coord.x == 1); + REQUIRE((*result)->coord.y == 2); + REQUIRE((*result)->coord.z == 3); + } + + SECTION("Insert and select with prepared statement") { + location loc{1, "center", {1, 2, 3}, Color::Black}; + + auto stmt = query::insert() + .into("location", column_generator::generate(schema, true)) + .values() + .prepare(db); + + auto res = stmt + .bind(loc) + .execute(); + REQUIRE(res.is_ok()); + REQUIRE(*res == 1); + + auto result = query::select(column_generator::generate(schema, true)) + .from("location") + .fetch_one(db); + + REQUIRE(result.is_ok()); + REQUIRE((*result)->name == "center"); + REQUIRE((*result)->color == Color::Black); + REQUIRE((*result)->coord.x == 1); + REQUIRE((*result)->coord.y == 2); + REQUIRE((*result)->coord.z == 3); + } +} \ No newline at end of file diff --git a/test/core/CMakeLists.txt b/test/core/CMakeLists.txt new file mode 100644 index 0000000..d44a1d3 --- /dev/null +++ b/test/core/CMakeLists.txt @@ -0,0 +1,25 @@ +CPMAddPackage("gh:catchorg/Catch2@3.7.1") + +list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) + +add_executable(CoreTests + utils/BasicTypeToVisitorTest.cpp + utils/ConvertTest.cpp + utils/DefaultTypeTraitsTest.cpp + utils/DependencyInjectionTest.cpp + utils/IdentifierTest.cpp + utils/ResultTest.cpp + utils/FieldAttributeTest.cpp + utils/VersionTest.cpp + utils/StringTest.cpp + object/PrototypeTreeTest.cpp +) + +target_link_libraries(CoreTests matador-core Catch2::Catch2WithMain) + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(CoreTests PRIVATE -coverage) + target_link_options(CoreTests PRIVATE -coverage) +endif () + +add_test(NAME CoreTests COMMAND CoreTests) diff --git a/test/core/object/PrototypeTreeTest.cpp b/test/core/object/PrototypeTreeTest.cpp new file mode 100644 index 0000000..00d71c3 --- /dev/null +++ b/test/core/object/PrototypeTreeTest.cpp @@ -0,0 +1,35 @@ +#include + +#include "matador/object/schema.hpp" + +struct node {}; + +using namespace matador; + +struct person { + virtual ~person() = default; +}; + +struct student final : person {}; +struct teacher final : person {}; + +TEST_CASE("Test empty prototype tree", "[prototype_tree][empty]") { + const object::schema tree; + + REQUIRE( tree.empty() ); +} + +TEST_CASE("Test add type to prototype tree", "[prototype_tree][add]") { + object::schema tree; + + REQUIRE( tree.empty() ); + + auto res = tree.attach("person"); + REQUIRE( res.is_ok() ); + res = tree.attach("student"); + REQUIRE( res.is_ok() ); + res = tree.attach("teacher"); + REQUIRE( res.is_ok() ); + + REQUIRE( tree.size() == 3 ); +} diff --git a/test/core/utils/BasicTypeToVisitorTest.cpp b/test/core/utils/BasicTypeToVisitorTest.cpp new file mode 100644 index 0000000..c3a716f --- /dev/null +++ b/test/core/utils/BasicTypeToVisitorTest.cpp @@ -0,0 +1,66 @@ +#include + +#include "matador/utils/types.hpp" +#include "matador/utils/basic_type_converter.hpp" + +using namespace matador::utils; + +TEST_CASE("Test convert any type to string", "[any type visitor]") { + basic_type_converter to_string_visitor; + + database_type value = 6; + auto res = basic_type_converter::convert_value(value); + REQUIRE(res.is_ok()); + REQUIRE(*res == "6"); + + value = 2.5; + res = basic_type_converter::convert_value(value); + REQUIRE(res.is_ok()); + REQUIRE(*res == "2.5"); + + value = true; + res = basic_type_converter::convert_value(value); + REQUIRE(res.is_ok()); + REQUIRE(*res == "true"); + + value = "hello"; + res = basic_type_converter::convert_value(value); + REQUIRE(res.is_ok()); + REQUIRE(*res == "hello"); + + value = std::string{"world"}; + res = basic_type_converter::convert_value(value); + REQUIRE(res.is_ok()); + REQUIRE(*res == "world");} + +TEST_CASE("Test convert any type to integral", "[any type visitor]") { + std::vector> values = { + {6, 6}, + {2.5, 2}, + {true, 1}, + {"hello", 0}, + {std::string{"world"}, 0} + }; + + for (const auto &value : values) { + const auto res = basic_type_converter::convert_value(value.first); + REQUIRE(res.is_ok()); + REQUIRE(*res == value.second); + } +} + +TEST_CASE("Test convert any type to floating point", "[any type visitor]") { + std::vector> values = { + {6, 6}, + {2.5, 2.5}, + {true, 1}, + {"hello", 0}, + {std::string{"world"}, 0} + }; + + for (const auto &value : values) { + const auto res = basic_type_converter::convert_value(value.first); + REQUIRE(res.is_ok()); + REQUIRE(*res == value.second); + } +} \ No newline at end of file diff --git a/test/core/utils/ConvertTest.cpp b/test/core/utils/ConvertTest.cpp new file mode 100644 index 0000000..9883be6 --- /dev/null +++ b/test/core/utils/ConvertTest.cpp @@ -0,0 +1,160 @@ +#include + +#include "matador/utils/convert.hpp" +//#include "matador/utils/date.hpp" +//#include "matador/utils/time.hpp" +#include "matador/utils/types.hpp" + +using namespace matador::utils; + +template +void validate_conversion(From from) +{ + auto res = to(from); + + REQUIRE(res.is_ok()); + REQUIRE(static_cast(from) == *res); +} + +template +void validate_conversion(const From &from, To expected_to) +{ + auto res = to(from); + + REQUIRE(res.is_ok()); + REQUIRE(*res == expected_to); +} + +template +void validate_conversion(const From &from, const std::string &expected_to) +{ + auto res = to(from); + + REQUIRE(res.is_ok()); + REQUIRE(*res == expected_to); +} + +template +void validate_conversion(const From &from, const char *expected_to) +{ + validate_conversion(from, std::string{expected_to}); +} + +template +void validate_integral_conversion(From from) { + validate_conversion(from); + validate_conversion(from); + validate_conversion(from); + validate_conversion(from); +} + +TEST_CASE("Test integral conversion", "[convert][integral]") { + validate_integral_conversion(-56); + validate_integral_conversion(-127); + validate_integral_conversion(-9876543); + validate_integral_conversion(-123456790); + + validate_integral_conversion(56); + validate_integral_conversion(127); + validate_integral_conversion(9876543); + validate_integral_conversion(123456790); + + validate_conversion(513, 1); + validate_conversion(515, 3); + validate_conversion(516, 4); +} +TEST_CASE("Test floating point conversion", "[convert][floating_point]") { + validate_conversion(-0.1f); + validate_conversion(-0.1f); + validate_conversion(-0.44444); + validate_conversion(-0.44444); +} + +TEST_CASE("Test integral to string conversion", "[convert][integral][string]") { + validate_conversion(-56, "-56"); + validate_conversion(-127, "-127"); + validate_conversion(-9876543, "-9876543"); + validate_conversion(-123456790, "-123456790"); + + validate_conversion(56, "56"); + validate_conversion(127, "127"); + validate_conversion(9876543, "9876543"); + validate_conversion(123456790, "123456790"); +} + +TEST_CASE("Test floating point to string conversion", "[convert][floating_point][string]") { + validate_conversion(-56.1234f, "-56.1234"); + validate_conversion(-127.444449, "-127.444449"); +} + +TEST_CASE("Test string to integral conversion", "[convert][string][integral]") { + validate_conversion("-56", -56); + validate_conversion("-127", -127); + validate_conversion("-9876543", -9876543); + validate_conversion("-123456790", -123456790); + + validate_conversion("56", 56); + validate_conversion("127", 127); + validate_conversion("9876543", 9876543); + validate_conversion("123456790", 123456790); + + validate_conversion("-56", -56); + validate_conversion("-127", -127); + validate_conversion("-9876543", -9876543); + validate_conversion("-123456790", -123456790); + + validate_conversion("56", 56); + validate_conversion("127", 127); + validate_conversion("9876543", 9876543); + validate_conversion("123456790", 123456790); +} + +TEST_CASE("Test string to floating point conversion", "[convert][string][floating_point]") { + validate_conversion("-56.1234", -56.1234f); + validate_conversion("-127.444449", -127.444449); + + validate_conversion("-56.1234", -56.1234f); + validate_conversion("-127.444449", -127.444449); +} + +TEST_CASE("Test blob to blob conversion", "[convert][blob]") { + blob from{1, 2, 3, 4}; + const auto res = to(from); + + REQUIRE(res.is_ok()); + REQUIRE(from == *res); +} + +// TEST_CASE("Validate date to string conversion", "[convert][date][string]") { +// matador::date today; +// const auto expected_string = matador::utils::to_string(today); +// std::string to; +// +// convert(to, today); +// +// REQUIRE(expected_string == to); +// } +// +// TEST_CASE("Validate date conversion leads to an exception", "[convert][date][exception]") { +// matador::date today; +// int to{}; +// convert(to, today); +// REQUIRE(to == today.julian_date()); +// } +// +// TEST_CASE("Validate time to string conversion", "[convert][time][string]") { +// matador::time now; +// const auto expected_string = matador::utils::to_string(now); +// std::string to; +// +// convert(to, now); +// +// REQUIRE(expected_string == to); +// } +// +// TEST_CASE("Validate time conversion leads to an exception", "[convert][time][exception]") { +// matador::time now; +// int to; +// convert(to, now); +// REQUIRE(to == now.get_time_info().seconds_since_epoch); +// } diff --git a/test/core/utils/DefaultTypeTraitsTest.cpp b/test/core/utils/DefaultTypeTraitsTest.cpp new file mode 100644 index 0000000..08b53ce --- /dev/null +++ b/test/core/utils/DefaultTypeTraitsTest.cpp @@ -0,0 +1,28 @@ +#include + +#include "matador/utils/default_type_traits.hpp" + +using namespace matador::utils; + +TEST_CASE("Test default data types", "[data_types][type]") { + REQUIRE(data_type_traits::type() == basic_type::type_int8); + REQUIRE(data_type_traits::type() == basic_type::type_int16); + REQUIRE(data_type_traits::type() == basic_type::type_int32); + REQUIRE(data_type_traits::type() == basic_type::type_int64); + REQUIRE(data_type_traits::type() == basic_type::type_uint8); + REQUIRE(data_type_traits::type() == basic_type::type_uint16); + REQUIRE(data_type_traits::type() == basic_type::type_uint32); + REQUIRE(data_type_traits::type() == basic_type::type_uint64); + REQUIRE(data_type_traits::type() == basic_type::type_bool); + REQUIRE(data_type_traits::type() == basic_type::type_float); + REQUIRE(data_type_traits::type() == basic_type::type_double); + REQUIRE(data_type_traits::type(32) == basic_type::type_varchar); + REQUIRE(data_type_traits::type(0) == basic_type::type_text); + REQUIRE(data_type_traits::type(32) == basic_type::type_varchar); + REQUIRE(data_type_traits::type(0) == basic_type::type_text); + REQUIRE(data_type_traits::type(32) == basic_type::type_varchar); + REQUIRE(data_type_traits::type(0) == basic_type::type_text); + REQUIRE(data_type_traits::type(32) == basic_type::type_varchar); + REQUIRE(data_type_traits::type(0) == basic_type::type_text); + REQUIRE(data_type_traits::type(0) == basic_type::type_blob); +} \ No newline at end of file diff --git a/test/core/utils/DependencyInjectionTest.cpp b/test/core/utils/DependencyInjectionTest.cpp new file mode 100644 index 0000000..b89461f --- /dev/null +++ b/test/core/utils/DependencyInjectionTest.cpp @@ -0,0 +1,113 @@ +#include + +#include "matador/utils/di.hpp" + +#include + +namespace detail { + +class greeter { +public: + virtual ~greeter() = default; + + [[nodiscard]] virtual std::string greet() const = 0; +}; + +class smart_greeter final : public greeter { +public: + [[nodiscard]] std::string greet() const override { return "hey dude"; } +}; + +class hello_greeter final : public greeter { +public: + [[nodiscard]] std::string greet() const override { return "hello"; } +}; + +class vehicle +{ +public: + virtual ~vehicle() = default; + + [[nodiscard]] virtual long id() const = 0; +}; + +class truck final : public vehicle +{ +public: + truck() : id_(++id_counter_) {} + + [[nodiscard]] long id() const override { return id_; } + +private: + static long id_counter_; + long id_{}; +}; + +long truck::id_counter_ = 0; + +class unknown {}; + +class per_thread { +public: + virtual ~per_thread() = default; + virtual void dump() = 0; +}; + +class per_thread_dumper final : public per_thread +{ +public: + explicit per_thread_dumper(std::string name ) : name_(std::move(name)) {} + void dump() override { + std::cout << name_ << ": thread id " << std::this_thread::get_id() << "\n"; + } + +private: + std::string name_; +}; + +} + +using namespace matador::utils; + +TEST_CASE("Test dependency injection", "[dependency-injection]") { + di::install_module([](di::module &module) { + module.bind()->to_singleton(); + }); + + di::inject g1; + + REQUIRE(g1); + + auto g2 = g1; + + REQUIRE(g2); + REQUIRE(g1 == g2); + + g2 = std::move(g1); + + REQUIRE(!g1); + REQUIRE(g2); + REQUIRE(g1 != g2); + + auto g3(std::move(g2)); + + REQUIRE(!g2); + REQUIRE(g3); + REQUIRE(g3 != g2); + + di::module m; + m.bind()->to_singleton(); + m.bind("smart")->to_singleton(); + + di::inject g4(m, "smart"); + REQUIRE(g4); + REQUIRE(g4->greet() == "hey dude"); + + di::inject g5(m); + REQUIRE(g5); + REQUIRE(g5->greet() == "hello"); + + REQUIRE(g4 != g5); + + // UNIT_ASSERT_EXCEPTION(matador::di::inject u, std::logic_error, "unknown type"); +} diff --git a/test/core/utils/FieldAttributeTest.cpp b/test/core/utils/FieldAttributeTest.cpp new file mode 100644 index 0000000..10e7064 --- /dev/null +++ b/test/core/utils/FieldAttributeTest.cpp @@ -0,0 +1,32 @@ +#include + +#include "matador/utils/field_attributes.hpp" + +using namespace matador::utils; + +TEST_CASE("Test field attribute", "[field-attribute]") { + field_attributes attr; + + REQUIRE(attr.size() == 0); + REQUIRE(attr.options() == constraints::NONE); + + attr = 255; + REQUIRE(attr.size() == 255); + REQUIRE(attr.options() == constraints::NONE); + + attr = constraints::INDEX; + REQUIRE(attr.size() == 0); + REQUIRE(attr.options() == constraints::INDEX); + + attr = { 255, constraints::DEFAULT }; + REQUIRE(attr.size() == 255); + REQUIRE(attr.options() == constraints::DEFAULT); + + field_attributes attr2{255}; + REQUIRE(attr2.size() == 255); + REQUIRE(attr2.options() == constraints::NONE); + + field_attributes attr3{constraints::UNIQUE}; + REQUIRE(attr3.size() == 0); + REQUIRE(attr3.options() == constraints::UNIQUE); +} \ No newline at end of file diff --git a/test/core/utils/IdentifierTest.cpp b/test/core/utils/IdentifierTest.cpp new file mode 100644 index 0000000..b6c9c1e --- /dev/null +++ b/test/core/utils/IdentifierTest.cpp @@ -0,0 +1,118 @@ +#include + +#include "matador/utils/identifier.hpp" +#include "matador/utils/default_type_traits.hpp" + +using namespace matador::utils; + +TEST_CASE("Test create identifier", "[identifier][create]") { + const identifier id; + + REQUIRE(id.is_null()); + REQUIRE(!id.is_integer()); + REQUIRE(!id.is_floating_point()); + REQUIRE(!id.is_bool()); + REQUIRE(!id.is_varchar()); + REQUIRE(!id.is_date()); + REQUIRE(!id.is_time()); + REQUIRE(!id.is_blob()); + REQUIRE(!id.is_valid()); + REQUIRE(id.str() == "null"); +} + +TEST_CASE("Test assign value to identifier", "[identifier][assign]") { + identifier id; + + REQUIRE(id.is_null()); + REQUIRE(!id.is_valid()); + REQUIRE(id.str() == "null"); + + id = 7; + + REQUIRE(!id.is_null()); + REQUIRE(id.is_valid()); + REQUIRE(id.is_integer()); + REQUIRE(id.str() == "7"); + REQUIRE(id.type() == basic_type::type_int32); + REQUIRE(id.type_index() == std::type_index(typeid(int))); + + id = std::string{"UniqueId"}; + + REQUIRE(!id.is_null()); + REQUIRE(id.is_valid()); + REQUIRE(id.is_varchar()); + REQUIRE(id.str() == "UniqueId"); + + id = "UniqueId"; + + REQUIRE(!id.is_null()); + REQUIRE(id.is_valid()); + REQUIRE(id.is_varchar()); + + // REQUIRE(id == identifier{"UniqueId"}); +} + +TEST_CASE("Test compare identifier", "[identifier][compare]") { + identifier id1{6}, id2{7}; + + REQUIRE(id1 != id2); + REQUIRE(id1 < id2); + REQUIRE(id1 <= id2); + REQUIRE(id2 > id1); + REQUIRE(id2 >= id1); + REQUIRE(id1.hash() != id2.hash()); + + id2 = 6; + + REQUIRE(id1 == id2); + REQUIRE(id1.hash() == id2.hash()); +} + +identifier create(const int id) { + return identifier{id}; +} + +TEST_CASE("Test copy identifier" "[identifier][copy]") { + identifier id1{"Unique"}; + REQUIRE(id1.is_valid()); + REQUIRE(id1.str() == "Unique"); + + const identifier id2(id1); + + REQUIRE(id1 == id2); + REQUIRE(id1.hash() == id2.hash()); + + id1.clear(); + + REQUIRE(id1.is_null()); + identifier id3 = id1; + + REQUIRE(id1 == id3); + REQUIRE(id1 < id3); + REQUIRE(id3.is_null()); + REQUIRE(id1.hash() == id3.hash()); + + id3 = create(9); + id3 = id1; +} + +TEST_CASE("Test move identifier", "[identifier][move]") { + identifier id1{6}; + + REQUIRE(id1.is_integer()); + REQUIRE(!id1.is_null()); + + const auto id2 = std::move(id1); + REQUIRE(id1.is_null()); + REQUIRE(id2.is_integer()); +} + +TEST_CASE("Test share identifier", "[identifier][share]") { + const identifier id1{6}; + + REQUIRE(id1.use_count() == 1); + + auto id2 = id1.share(); + REQUIRE(id1 == id2); + REQUIRE(id1.use_count() == 2); +} \ No newline at end of file diff --git a/test/core/utils/ResultTest.cpp b/test/core/utils/ResultTest.cpp new file mode 100644 index 0000000..4be5638 --- /dev/null +++ b/test/core/utils/ResultTest.cpp @@ -0,0 +1,176 @@ +#include + +#include + +#include "matador/utils/result.hpp" + +namespace matador::test { +enum class math_error : int32_t { + OK = 0, + DIVISION_BY_ZERO = 1, + FAILURE = 2 +}; + +utils::resultdivide(const float x, const float y) { + if (y == 0) { + return utils::failure(math_error::DIVISION_BY_ZERO); + } + + return utils::ok(x / y); +} + +utils::resultmultiply(const float x, const float y) { + return utils::ok(x * y); +} + +utils::resultplus(const float x, const float y) { + return utils::ok(x + y); +} + +utils::resultaction_on_greater_42(const float i) { + if (i > 42) { + return utils::ok(); + } + return utils::failure(math_error::FAILURE); +} + +} + +using namespace matador; + +TEST_CASE("Test result", "[result]") { + auto res = test::divide(4, 2); + REQUIRE(res); + REQUIRE(res.is_ok()); + REQUIRE(!res.is_error()); + + REQUIRE((res.value() == 2.0)); + + REQUIRE_THROWS(res.err()); + + res = test::divide(4, 0); + REQUIRE(!res); + REQUIRE(!res.is_ok()); + REQUIRE(res.is_error()); + + REQUIRE((res.err() == test::math_error::DIVISION_BY_ZERO)); + + res = test::divide(4, 2) + .and_then([](const auto &val) { return test::multiply(val, 5); }) + .and_then([](const auto &val) { return test::plus(val, 10); }); + + REQUIRE(res); + REQUIRE(res.is_ok()); + REQUIRE(!res.is_error()); + REQUIRE((res.value() == 20.0)); + + res = test::divide(4, 0) + .and_then([](const auto &val) { + return test::multiply(val, 5); + }); + + REQUIRE(!res); + REQUIRE(!res.is_ok()); + REQUIRE(res.is_error()); + REQUIRE((res.err() == test::math_error::DIVISION_BY_ZERO)); + + auto res2 = test::divide(4, 0) + .or_else([](const auto &err) { + switch (err) { + case test::math_error::DIVISION_BY_ZERO: + return utils::failure(std::string("division by zero error")); + default: + return utils::failure(std::string("unknown error")); + } + }); + + REQUIRE(!res2); + REQUIRE(!res2.is_ok()); + REQUIRE(res2.is_error()); + const auto e = res2.err(); +// REQUIRE(res2.err() == "division by zero error"); + + res = test::divide(4, 2) + .and_then([](const auto &val) { return test::multiply(val, 5); }) + .map([](const auto &val) { return val + 10; }); + + REQUIRE(res); + REQUIRE(res.is_ok()); + REQUIRE(!res.is_error()); + REQUIRE((res.value() == 20.0)); + + auto res_void = test::action_on_greater_42(43); + REQUIRE(res_void); + REQUIRE(res_void.is_ok()); + REQUIRE(!res_void.is_error()); + + res_void = test::action_on_greater_42(41); + REQUIRE(!res_void); + REQUIRE(!res_void.is_ok()); + REQUIRE(res_void.is_error()); + + auto res_float = test::divide(4, 2) + .and_then([](const auto &val) { return test::action_on_greater_42(val); }); + REQUIRE(!res_float); + REQUIRE(!res_float.is_ok()); + REQUIRE(res_float.is_error()); + + res_void = test::divide(120, 2) + .and_then([](const auto &val) { return test::action_on_greater_42(val); }); + REQUIRE(res_void); + REQUIRE(res_void.is_ok()); + REQUIRE(!res_void.is_error()); +} + +namespace matador::test { +class CustomError { +public: + CustomError( const math_error err, std::string msg) + : error_(err), message_(std::move(msg)) {} + + [[nodiscard]] math_error error() const { return error_; } + [[nodiscard]] std::string message() const { return message_; } + +private: + math_error error_{}; + std::string message_; +}; + +utils::result custom_divide(const float x, const float y) { + utils::result res = divide(x, y); + + if (res.is_ok()) { + return utils::ok(res.value()); + } + + return utils::failure({res.err(), "ERROR"}); +} +} + +TEST_CASE("Test result with custom error", "[result][custom]") { + auto res = test::custom_divide(4, 2); + + REQUIRE(res.is_ok()); + REQUIRE(!res.is_error()); + + res = test::custom_divide(4, 0); + + REQUIRE(!res.is_ok()); + REQUIRE(res.is_error()); + + const auto err = res.err(); + + REQUIRE(err.error() == test::math_error::DIVISION_BY_ZERO); + REQUIRE(err.message() == "ERROR"); +} + +TEST_CASE("Test result with void type", "[result][void]") { + auto res = test::action_on_greater_42(43); + + REQUIRE(res.is_ok()); + + res = test::action_on_greater_42(41); + + REQUIRE(res.is_error()); + REQUIRE(res.err() == test::math_error::FAILURE); +} \ No newline at end of file diff --git a/test/core/utils/StringTest.cpp b/test/core/utils/StringTest.cpp new file mode 100644 index 0000000..36466bb --- /dev/null +++ b/test/core/utils/StringTest.cpp @@ -0,0 +1,28 @@ +#include + +#include "matador/utils/string.hpp" + +using namespace matador::utils; + +TEST_CASE("Test splitting of string", "[string][split]") { + const std::string str("1,2,3,5,6"); + + std::vector string_vec; + + const size_t count = split(str, ',', string_vec); + + REQUIRE(count == 5); + REQUIRE(string_vec.size() == 5); +} + +TEST_CASE("Test trimming of string", "[string][trim]") { + std::string str(" middle "); + + std::string result = trim(str); + + REQUIRE(result == "middle"); + + result = trim(str, "-"); + + REQUIRE(result == str); +} diff --git a/test/core/utils/VersionTest.cpp b/test/core/utils/VersionTest.cpp new file mode 100644 index 0000000..562259d --- /dev/null +++ b/test/core/utils/VersionTest.cpp @@ -0,0 +1,62 @@ +#include + +#include "matador/utils/version.hpp" + +using namespace matador::utils; + +TEST_CASE("Test version interface", "[version][interface]") { + version v0; + + REQUIRE(v0.major() == 0); + REQUIRE(v0.minor() == 0); + REQUIRE(v0.patch() == 0); + REQUIRE(v0.str() == "0.0.0"); + + v0.major(1); + v0.minor(2); + v0.patch(3); + + REQUIRE(v0.major() == 1); + REQUIRE(v0.minor() == 2); + REQUIRE(v0.patch() == 3); + REQUIRE(v0.str() == "1.2.3"); + + version v02; + + REQUIRE(v02 < v0); + REQUIRE(v02 <= v0); + REQUIRE(v0 > v02); + REQUIRE(v0 >= v02); + REQUIRE(v02 != v0); + + version v1 = v0; + + REQUIRE(v1 == v0); + + version v2(v1); + + REQUIRE(v2 == v1); +} + +TEST_CASE("Test version parsing", "[version][parse]") { + const auto version_str{"13.67.34"}; + const auto v1 = version::from_string(version_str); + REQUIRE(v1.is_ok()); + + REQUIRE(v1->major() == 13); + REQUIRE(v1->minor() == 67); + REQUIRE(v1->patch() == 34); + REQUIRE(v1->str() == version_str); + + const auto v2 = version::from_string("01.02.03"); + REQUIRE(v2.is_ok()); + + REQUIRE(v2->major() == 1); + REQUIRE(v2->minor() == 2); + REQUIRE(v2->patch() == 3); + REQUIRE(v2->str() == "1.2.3"); + + const auto v3 = version::from_string("a.b.c"); + REQUIRE(v3.is_error()); + REQUIRE(v3.err().message() == "Invalid version string"); +} diff --git a/test/models/airplane.hpp b/test/models/airplane.hpp deleted file mode 100644 index 10ad424..0000000 --- a/test/models/airplane.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef QUERY_AIRPLANE_HPP -#define QUERY_AIRPLANE_HPP - -#include "category.hpp" -#include "supplier.hpp" - -#include "matador/utils/access.hpp" -#include "matador/utils/cascade_type.hpp" -#include "matador/utils/field_attributes.hpp" - -#include "matador/sql/entity.hpp" - -#include - -namespace matador::test { - -struct airplane -{ - airplane() = default; - airplane(unsigned long id, std::string b, std::string m) - : id(id) - , brand(std::move(b)) - , model(std::move(m)) {} - unsigned long id{}; - std::string brand; - std::string model; - - template - void process(Operator &op) { - namespace field = matador::utils::access; - using namespace matador::utils; - field::primary_key(op, "id", id); - field::attribute(op, "brand", brand, 255); - field::attribute(op, "model", model, 255); - } -}; - -} - -#endif //QUERY_AIRPLANE_HPP diff --git a/test/models/author.hpp b/test/models/author.hpp deleted file mode 100644 index fc0599e..0000000 --- a/test/models/author.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef QUERY_AUTHOR_HPP -#define QUERY_AUTHOR_HPP - -#include "matador/utils/access.hpp" - -#include "matador/sql/entity.hpp" - -#include - -namespace matador::test { - -struct book; - -struct author -{ - unsigned long id{}; - std::string first_name; - std::string last_name; - std::string date_of_birth; - unsigned short year_of_birth{}; - bool distinguished{false}; - std::vector> books; - - template - void process(Operator &op) - { - namespace field = matador::utils::access; - field::primary_key(op, "id", id); - field::attribute(op, "first_name", first_name, 63); - field::attribute(op, "last_name", last_name, 63); - field::attribute(op, "date_of_birth", date_of_birth, 31); - field::attribute(op, "year_of_birth", year_of_birth); - field::attribute(op, "distinguished", distinguished); - field::has_many(op, books, "author_id", utils::fetch_type::LAZY); - } -}; - -} - -#endif //QUERY_AUTHOR_HPP diff --git a/test/models/book.hpp b/test/models/book.hpp deleted file mode 100644 index 2e645b8..0000000 --- a/test/models/book.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef QUERY_BOOK_HPP -#define QUERY_BOOK_HPP - -#include "matador/sql/entity.hpp" - -#include "matador/utils/access.hpp" -#include "matador/utils/foreign_attributes.hpp" - -#include - -namespace matador::test { - -struct author; - -struct book -{ - unsigned long id{}; - matador::sql::entity book_author; - std::string title; - unsigned short published_in{}; - - template - void process(Operator &op) - { - namespace field = matador::utils::access; - field::primary_key(op, "id", id); - field::attribute(op, "title", title, 511); - field::belongs_to(op, "author_id", book_author, utils::fetch_type::EAGER); - field::attribute(op, "published_in", published_in); - } -}; - -} -#endif //QUERY_BOOK_HPP diff --git a/test/models/category.hpp b/test/models/category.hpp deleted file mode 100644 index 38d3ea0..0000000 --- a/test/models/category.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef QUERY_CATEGORY_HPP -#define QUERY_CATEGORY_HPP - -#include "matador/utils/access.hpp" - -#include - -namespace matador::test { - -struct category -{ - unsigned long id{}; - std::string name; - - template - void process(Operator &op) { - namespace field = matador::utils::access; - using namespace matador::utils; - field::primary_key(op, "id", id); - field::attribute(op, "name", name, 255); - } -}; - -} -#endif //QUERY_CATEGORY_HPP diff --git a/test/models/coordinate.hpp b/test/models/coordinate.hpp index 59dbd07..6165c91 100644 --- a/test/models/coordinate.hpp +++ b/test/models/coordinate.hpp @@ -2,6 +2,7 @@ #define QUERY_COORDINATE_HPP #include "matador/utils/access.hpp" +#include "matador/utils/field_attributes.hpp" namespace matador::test { struct coordinate @@ -13,10 +14,10 @@ struct coordinate } -namespace matador::utils::access { +namespace matador::access { template -void attribute(Operator &op, const char *id, test::coordinate &value, const field_attributes &attr = null_attributes) { +void attribute(Operator &op, const char *id, test::coordinate &value, const utils::field_attributes &attr = utils::null_attributes) { attribute(op, (std::string(id) + "_x").c_str(), value.x, attr); attribute(op, (std::string(id) + "_y").c_str(), value.y, attr); attribute(op, (std::string(id) + "_z").c_str(), value.z, attr); diff --git a/test/models/flight.hpp b/test/models/flight.hpp deleted file mode 100644 index 0b24460..0000000 --- a/test/models/flight.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef QUERY_FLIGHT_HPP -#define QUERY_FLIGHT_HPP - -#include "airplane.hpp" - -#include "matador/utils/access.hpp" -#include "matador/utils/cascade_type.hpp" -#include "matador/utils/fetch_type.hpp" -#include "matador/utils/field_attributes.hpp" - -#include "matador/sql/entity.hpp" - -#include -#include - -namespace matador::test { - -struct flight -{ - flight() = default; - flight(unsigned long id, const sql::entity &plane, std::string name) - : id(id), airplane(plane), pilot_name(std::move(name)) {} - - unsigned long id{}; - sql::entity airplane; - std::string pilot_name; - - template - void process(Operator &op) { - namespace field = matador::utils::access; - using namespace matador::utils; - field::primary_key(op, "id", id); - field::has_one(op, "airplane_id", airplane, {utils::cascade_type::ALL, utils::fetch_type::EAGER}); - field::attribute(op, "pilot_name", pilot_name, 255); - } -}; - -} - -#endif //QUERY_FLIGHT_HPP diff --git a/test/models/location.hpp b/test/models/location.hpp index e15b431..0506cac 100644 --- a/test/models/location.hpp +++ b/test/models/location.hpp @@ -5,6 +5,8 @@ #include "coordinate.hpp" +#include + namespace matador::test { enum class Color : uint8_t { @@ -21,7 +23,7 @@ struct location template < class Operator > void process(Operator &op) { - namespace field = matador::utils::access; + namespace field = matador::access; field::primary_key(op, "id", id); field::attribute(op, "name", name, 255); field::attribute(op, "coordinate", coord); diff --git a/test/models/optional.hpp b/test/models/optional.hpp deleted file mode 100644 index 88a59f2..0000000 --- a/test/models/optional.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef QUERY_OPTIONAL_HPP -#define QUERY_OPTIONAL_HPP - -#include "matador/utils/access.hpp" -#include "matador/utils/field_attributes.hpp" - -#include -#include - -namespace matador::test { - -struct optional -{ - unsigned long id{}; - std::optional name; - std::optional age{}; - - template - void process(Operator &op) { - namespace field = matador::utils::access; - using namespace matador::utils; - field::primary_key(op, "id", id); - field::attribute(op, "name", name, 255); - field::attribute(op, "age", age); - } -}; - -} - -#endif //QUERY_OPTIONAL_HPP diff --git a/test/models/order.hpp b/test/models/order.hpp deleted file mode 100644 index 64d5f12..0000000 --- a/test/models/order.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef QUERY_ORDER_HPP -#define QUERY_ORDER_HPP - -#include "order_details.hpp" - -#include "matador/utils/access.hpp" - -#include "matador/sql/entity.hpp" - -#include - -namespace matador::test { - -struct order -{ - unsigned long order_id{}; - std::string order_date; - std::string required_date; - std::string shipped_date; - unsigned int ship_via{}; - unsigned int freight{}; - std::string ship_name; - std::string ship_address; - std::string ship_city; - std::string ship_region; - std::string ship_postal_code; - std::string ship_country; - std::vector> order_details_; - - template - void process(Operator &op) { - namespace field = matador::utils::access; - field::primary_key(op, "order_id", order_id); - field::attribute(op, "order_date", order_date, 255); - field::attribute(op, "required_date", required_date, 255); - field::attribute(op, "shipped_date", shipped_date, 255); - field::attribute(op, "ship_via", ship_via); - field::attribute(op, "freight", freight); - field::attribute(op, "ship_name", ship_name, 255); - field::attribute(op, "ship_address", ship_address, 255); - field::attribute(op, "ship_city", ship_city, 255); - field::attribute(op, "ship_region", ship_region, 255); - field::attribute(op, "ship_postal_code", ship_postal_code, 255); - field::attribute(op, "ship_country", ship_country, 255); - field::has_many(op, order_details_, "order_id", utils::fetch_type::EAGER); - } -}; - -} -#endif //QUERY_ORDER_HPP diff --git a/test/models/order_details.hpp b/test/models/order_details.hpp deleted file mode 100644 index 3a7dba0..0000000 --- a/test/models/order_details.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef QUERY_ORDER_DETAILS_HPP -#define QUERY_ORDER_DETAILS_HPP - -#include "product.hpp" - -#include "matador/sql/entity.hpp" - -namespace matador::test { - -struct order; - -struct order_details -{ - unsigned long order_details_id; - sql::entity order_; - sql::entity product_; - - template - void process(Operator &op) { - namespace field = matador::utils::access; - field::primary_key(op, "order_details_id", order_details_id); - field::belongs_to(op, "order_id", order_, utils::default_foreign_attributes); - field::has_one(op, "product_id", product_, utils::default_foreign_attributes); - } -}; - -} -#endif //QUERY_ORDER_DETAILS_HPP diff --git a/test/models/person.hpp b/test/models/person.hpp deleted file mode 100644 index 103e2e5..0000000 --- a/test/models/person.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef QUERY_PERSON_HPP -#define QUERY_PERSON_HPP - -#include "matador/utils/access.hpp" -#include "matador/utils/field_attributes.hpp" -#include "matador/utils/types.hpp" - -#include - -namespace matador::test { - -struct person -{ - unsigned long id{}; - std::string name; - unsigned int age{}; - utils::blob image; - - template - void process(Operator &op) { - namespace field = matador::utils::access; - using namespace matador::utils; - field::primary_key(op, "id", id); - field::attribute(op, "name", name, 255); - field::attribute(op, "age", age); - field::attribute(op, "image", image); - } -}; - -} -#endif //QUERY_PERSON_HPP diff --git a/test/models/product.hpp b/test/models/product.hpp deleted file mode 100644 index b0b0740..0000000 --- a/test/models/product.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef QUERY_PRODUCT_HPP -#define QUERY_PRODUCT_HPP - -#include "category.hpp" -#include "supplier.hpp" - -#include "matador/utils/access.hpp" -#include "matador/utils/cascade_type.hpp" -#include "matador/utils/field_attributes.hpp" - -#include "matador/sql/entity.hpp" - -#include - -namespace matador::test { - -struct product -{ - std::string product_name; - sql::entity supplier; - sql::entity category; - std::string quantity_per_unit; - unsigned int unit_price; - unsigned int units_in_stock; - unsigned int units_in_order; - unsigned int reorder_level; - bool discontinued; - - template - void process(Operator &op) { - namespace field = matador::utils::access; - using namespace matador::utils; - field::primary_key(op, "product_name", product_name, 255); - field::has_one(op, "supplier_id", supplier, utils::cascade_type::ALL); - field::has_one(op, "category_id", category, utils::cascade_type::ALL); - field::attribute(op, "quantity_per_unit", quantity_per_unit, 255); - field::attribute(op, "unit_price", unit_price); - field::attribute(op, "units_in_stock", units_in_stock); - field::attribute(op, "units_in_order", units_in_order); - field::attribute(op, "reorder_level", reorder_level); - field::attribute(op, "discontinued", discontinued); - } -}; -} - -#endif //QUERY_PRODUCT_HPP diff --git a/test/models/recipe.hpp b/test/models/recipe.hpp deleted file mode 100644 index f43ee17..0000000 --- a/test/models/recipe.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef QUERY_RECIPE_HPP -#define QUERY_RECIPE_HPP - -#include "matador/utils/access.hpp" -#include "matador/utils/field_attributes.hpp" -#include "matador/utils/foreign_attributes.hpp" - -#include "matador/sql/entity.hpp" -#include "matador/sql/has_many_to_many_relation.hpp" - -#include - -namespace matador::test { - -struct recipe; -struct ingredient -{ - unsigned long id{}; - std::string name; - std::vector> recipes; - - template - void process(Operator &op) { - namespace field = matador::utils::access; - field::primary_key(op, "id", id); - field::attribute(op, "name", name, 255); - field::has_many_to_many(op, "recipe_ingredients", recipes, "ingredient_id", "recipe_id", utils::fetch_type::EAGER); - } -}; - -struct recipe -{ - unsigned long id{}; - std::string name; - std::vector> ingredients; - - template - void process(Operator &op) { - namespace field = matador::utils::access; - field::primary_key(op, "id", id); - field::attribute(op, "name", name, 255); - field::has_many_to_many(op, "recipe_ingredients", ingredients, utils::fetch_type::LAZY); - } -}; - -class recipe_ingredient : public sql::has_many_to_many_relation -{ -public: - recipe_ingredient() - : has_many_to_many_relation("recipe_id", "ingredient_id") {} -}; - -} - -#endif //QUERY_RECIPE_HPP diff --git a/test/models/student.hpp b/test/models/student.hpp deleted file mode 100644 index b1b07a4..0000000 --- a/test/models/student.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef QUERY_STUDENT_HPP -#define QUERY_STUDENT_HPP - -#include "matador/utils/access.hpp" -#include "matador/utils/foreign_attributes.hpp" - -#include "matador/sql/entity.hpp" -#include "matador/sql/has_many_to_many_relation.hpp" - -#include -#include - -namespace matador::test { - -class course; - -struct student -{ - unsigned long id{}; - std::string name; - std::vector> courses; - - student() = default; - explicit student(unsigned long id, std::string name) - : id(id) - , name(std::move(name)) {} - - template < class Operator > - void process(Operator &op) - { - namespace field = matador::utils::access; - field::primary_key(op, "id", id); - field::attribute(op, "name", name, 255); - field::has_many_to_many(op, "student_courses", courses, "student_id", "course_id", utils::fetch_type::LAZY); - } - -}; - -struct course -{ - unsigned long id{}; - std::string title; - std::vector> students; - - course() = default; - explicit course(unsigned long id, std::string title) - : id(id) - , title(std::move(title)) {} - - template < class Operator > - void process(Operator &op) - { - namespace field = matador::utils::access; - field::primary_key(op, "id", id); - field::attribute(op, "title", title, 255); - field::has_many_to_many(op, "student_courses", students, utils::fetch_type::EAGER); - } -}; - -class student_course : public sql::has_many_to_many_relation -{ -public: - student_course() - : has_many_to_many_relation("student_id", "course_id") {} -}; - -} -#endif //QUERY_STUDENT_HPP diff --git a/test/models/supplier.hpp b/test/models/supplier.hpp deleted file mode 100644 index 8035e18..0000000 --- a/test/models/supplier.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef QUERY_SUPPLIER_HPP -#define QUERY_SUPPLIER_HPP - -#include "matador/utils/access.hpp" - -#include - -namespace matador::test { - -struct supplier -{ - unsigned long id{}; - std::string name; - - template - void process(Operator &op) { - namespace field = matador::utils::access; - using namespace matador::utils; - field::primary_key(op, "id", id); - field::attribute(op, "name", name, 255); - } -}; - -} -#endif //QUERY_SUPPLIER_HPP diff --git a/test/models/types.hpp b/test/models/types.hpp new file mode 100644 index 0000000..35e244e --- /dev/null +++ b/test/models/types.hpp @@ -0,0 +1,64 @@ +#ifndef QUERY_TYPES_HPP +#define QUERY_TYPES_HPP + +#include "matador/utils/access.hpp" +#include "matador/utils/types.hpp" + +namespace matador::test { + +struct types +{ + enum { CSTR_LEN=255 }; + + unsigned long id_ = 0; + char char_ = 'c'; + short short_ = -127; + int int_ = -65000; + long long_ = -128000; + long long long64_ = -1234567890; + unsigned char unsigned_char_ = 'H'; + unsigned short unsigned_short_ = 128; + unsigned int unsigned_int_ = 65000; + unsigned long unsigned_long_ = 128000; + unsigned long long unsigned_long64_ = 1234567890; + float float_ = 3.1415f; + double double_ = 1.1414; + bool bool_ = true; + char cstr_[CSTR_LEN]{}; + std::string string_ = "Welt"; + std::string varchar_ = "Erde"; + matador::date date_; + matador::time time_; + utils::blob binary_{ 1, 2, 3, 4 }; + + template < class Operator > + void process(Operator &op) + { + namespace field = matador::access; + using namespace matador::utils; + field::primary_key(op, "id", id_); + field::attribute(op, "val_char", char_); + field::attribute(op, "val_float", float_); + field::attribute(op, "val_double", double_); + field::attribute(op, "val_short", short_); + field::attribute(op, "val_int", int_); + field::attribute(op, "val_long", long_); + field::attribute(op, "val_long_long", long64_); + field::attribute(op, "val_unsigned_char", unsigned_char_); + field::attribute(op, "val_unsigned_short", unsigned_short_); + field::attribute(op, "val_unsigned_int", unsigned_int_); + field::attribute(op, "val_unsigned_long", unsigned_long_); + field::attribute(op, "val_unsigned_long_long", unsigned_long64_); + field::attribute(op, "val_bool", bool_); + field::attribute(op, "val_cstr", cstr_, CSTR_LEN); + field::attribute(op, "val_string", string_); + field::attribute(op, "val_varchar", varchar_, 63); + field::attribute(op, "val_date", date_); + field::attribute(op, "val_time", time_); + field::attribute(op, "val_binary", binary_); + } +}; + +} + +#endif // QUERY_TYPES_HPP diff --git a/test/orm/CMakeLists.txt b/test/orm/CMakeLists.txt new file mode 100644 index 0000000..3ebfee5 --- /dev/null +++ b/test/orm/CMakeLists.txt @@ -0,0 +1,30 @@ +CPMAddPackage("gh:catchorg/Catch2@3.7.1") + +list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) + +add_executable(OrmTests + backend/test_backend_service.cpp + backend/test_backend_service.hpp + backend/test_connection.cpp + backend/test_connection.hpp + query/ConditionTests.cpp + query/QueryBuilderTest.cpp + query/QueryFixture.cpp + query/QueryFixture.hpp + sql/ColumnTest.cpp + sql/FieldTest.cpp + backend/test_result_reader.cpp + backend/test_result_reader.hpp + backend/test_statement.cpp + backend/test_statement.hpp + backend/test_parameter_binder.cpp + backend/test_parameter_binder.hpp + query/QueryTest.cpp +) + +target_link_libraries(OrmTests matador-orm matador-core Catch2::Catch2WithMain) + +target_compile_options(OrmTests PRIVATE -coverage) +target_link_options(OrmTests PRIVATE -coverage) + +add_test(NAME OrmTests COMMAND OrmTests) diff --git a/test/orm/backend/test_backend_service.cpp b/test/orm/backend/test_backend_service.cpp new file mode 100644 index 0000000..729e627 --- /dev/null +++ b/test/orm/backend/test_backend_service.cpp @@ -0,0 +1,31 @@ +#include "test_backend_service.hpp" +#include "test_connection.hpp" + +#include "matador/sql/dialect_builder.hpp" + +#include + +namespace matador::test::orm { + +sql::connection_impl *test_backend_service::create(const sql::connection_info &info) +{ + return noop_connections_.insert(std::make_unique(info)).first->get(); +} + +void test_backend_service::destroy(sql::connection_impl *impl) +{ + auto it = std::find_if(noop_connections_.begin(), noop_connections_.end(), [impl](const auto &item) { + return impl == item.get(); + }); + if (it != noop_connections_.end()) { + noop_connections_.erase(it); + } +} + +const sql::dialect *test_backend_service::dialect() const +{ + static sql::dialect dialect_ = sql::dialect_builder::builder().create().build(); + return &dialect_; +} + +} \ No newline at end of file diff --git a/test/orm/backend/test_backend_service.hpp b/test/orm/backend/test_backend_service.hpp new file mode 100644 index 0000000..0c31816 --- /dev/null +++ b/test/orm/backend/test_backend_service.hpp @@ -0,0 +1,22 @@ +#ifndef NOOP_BACKEND_SERVICE_HPP +#define NOOP_BACKEND_SERVICE_HPP + +#include "matador/sql/backend_provider.hpp" + +#include + +namespace matador::test::orm { + +class test_backend_service final : public sql::backend_provider::basic_backend_service { +public: + sql::connection_impl *create(const sql::connection_info &info) override; + void destroy(sql::connection_impl *impl) override; + [[nodiscard]] const sql::dialect *dialect() const override; + +private: + std::unordered_set> noop_connections_; +}; + +} + +#endif //NOOP_BACKEND_SERVICE_HPP diff --git a/test/orm/backend/test_connection.cpp b/test/orm/backend/test_connection.cpp new file mode 100644 index 0000000..18b585f --- /dev/null +++ b/test/orm/backend/test_connection.cpp @@ -0,0 +1,80 @@ +#include "test_connection.hpp" +#include "test_result_reader.hpp" + +#include "matador/sql/query_context.hpp" +#include "matador/sql/record.hpp" + +#include "matador/sql/internal/query_result_impl.hpp" + +#include "matador/sql/interface/statement_impl.hpp" + +#include "matador/utils/string.hpp" + +#include +#include + +namespace matador::test::orm { +test_connection::test_connection(const sql::connection_info &info) +: connection_impl(info) {} + +utils::result test_connection::open() +{ + is_open_ = true; + return utils::ok(); +} + +utils::result test_connection::close() +{ + is_open_ = false; + return utils::ok(); +} + +utils::result test_connection::is_open() const +{ + return utils::ok(is_open_); +} + +utils::result test_connection::is_valid() const +{ + return is_open(); +} + +utils::result test_connection::client_version() const { + return utils::ok(utils::version{1, 2, 3}); +} + +utils::result test_connection::server_version() const { + return utils::ok(utils::version{3, 2, 1}); +} + +utils::result test_connection::execute(const std::string &/*stmt*/) +{ + return utils::ok(static_cast(4)); +} + +utils::result, utils::error> test_connection::fetch(const sql::query_context &context) +{ + return utils::ok(std::make_unique(std::make_unique(), context.prototype, context.prototype.size())); +} + +utils::result, utils::error> test_connection::prepare(const sql::query_context &/*context*/) +{ + return utils::ok(std::unique_ptr{}); +} + +utils::result, utils::error> test_connection::describe(const std::string &/*table*/) +{ + return utils::ok(std::vector{}); +} + +utils::result test_connection::exists(const std::string &/*schema_name*/, const std::string &/*table_name*/) +{ + return utils::ok(false); +} + +std::string test_connection::to_escaped_string(const utils::blob& value) const +{ + return utils::to_string(value); +} + +} \ No newline at end of file diff --git a/test/orm/backend/test_connection.hpp b/test/orm/backend/test_connection.hpp new file mode 100644 index 0000000..5909d54 --- /dev/null +++ b/test/orm/backend/test_connection.hpp @@ -0,0 +1,32 @@ +#ifndef QUERY_NOOP_CONNECTION_HPP +#define QUERY_NOOP_CONNECTION_HPP + +#include "matador/sql/interface/connection_impl.hpp" + +namespace matador::test::orm { + +class test_connection final : public sql::connection_impl +{ +public: + explicit test_connection(const sql::connection_info &info); + + utils::result open() override; + utils::result close() override; + [[nodiscard]] utils::result is_open() const override; + [[nodiscard]] utils::result is_valid() const override; + [[nodiscard]] utils::result client_version() const override; + [[nodiscard]] utils::result server_version() const override; + utils::result execute(const std::string &stmt) override; + utils::result, utils::error> fetch(const sql::query_context &context) override; + utils::result, utils::error> prepare(const sql::query_context &context) override; + utils::result, utils::error> describe(const std::string &table) override; + utils::result exists(const std::string &schema_name, const std::string &table_name) override; + + [[nodiscard]] std::string to_escaped_string( const utils::blob& value ) const override; + +private: + bool is_open_{false}; +}; + +} +#endif //QUERY_NOOP_CONNECTION_HPP diff --git a/test/orm/backend/test_parameter_binder.cpp b/test/orm/backend/test_parameter_binder.cpp new file mode 100644 index 0000000..08a89e7 --- /dev/null +++ b/test/orm/backend/test_parameter_binder.cpp @@ -0,0 +1,23 @@ +#include "test_parameter_binder.hpp" + +namespace matador::test::orm { +void test_parameter_binder::write_value(size_t pos, const int8_t &x) {} +void test_parameter_binder::write_value(size_t pos, const int16_t &x) {} +void test_parameter_binder::write_value(size_t pos, const int32_t &x) {} +void test_parameter_binder::write_value(size_t pos, const int64_t &x) {} +void test_parameter_binder::write_value(size_t pos, const uint8_t &x) {} +void test_parameter_binder::write_value(size_t pos, const uint16_t &x) {} +void test_parameter_binder::write_value(size_t pos, const uint32_t &x) {} +void test_parameter_binder::write_value(size_t pos, const uint64_t &x) {} +void test_parameter_binder::write_value(size_t pos, const bool &x) {} +void test_parameter_binder::write_value(size_t pos, const float &x) {} +void test_parameter_binder::write_value(size_t pos, const double &x) {} +void test_parameter_binder::write_value(size_t pos, const time &x) {} +void test_parameter_binder::write_value(size_t pos, const date &x) {} +void test_parameter_binder::write_value(size_t pos, const char *x) {} +void test_parameter_binder::write_value(size_t pos, const char *x, size_t size) {} +void test_parameter_binder::write_value(size_t pos, const std::string &x) {} +void test_parameter_binder::write_value(size_t pos, const std::string &x, size_t size) {} +void test_parameter_binder::write_value(size_t pos, const utils::blob &x) {} +void test_parameter_binder::write_value(size_t pos, const utils::value &x, size_t size) {} +} diff --git a/test/orm/backend/test_parameter_binder.hpp b/test/orm/backend/test_parameter_binder.hpp new file mode 100644 index 0000000..3110bf7 --- /dev/null +++ b/test/orm/backend/test_parameter_binder.hpp @@ -0,0 +1,33 @@ +#ifndef TEST_PARAMETER_BINDER_HPP +#define TEST_PARAMETER_BINDER_HPP + +#include "matador/utils/attribute_writer.hpp" + +namespace matador::test::orm { + +class test_parameter_binder final : public utils::attribute_writer { +public: + void write_value(size_t pos, const int8_t &x) override; + void write_value(size_t pos, const int16_t &x) override; + void write_value(size_t pos, const int32_t &x) override; + void write_value(size_t pos, const int64_t &x) override; + void write_value(size_t pos, const uint8_t &x) override; + void write_value(size_t pos, const uint16_t &x) override; + void write_value(size_t pos, const uint32_t &x) override; + void write_value(size_t pos, const uint64_t &x) override; + void write_value(size_t pos, const bool &x) override; + void write_value(size_t pos, const float &x) override; + void write_value(size_t pos, const double &x) override; + void write_value(size_t pos, const time &x) override; + void write_value(size_t pos, const date &x) override; + void write_value(size_t pos, const char *x) override; + void write_value(size_t pos, const char *x, size_t size) override; + void write_value(size_t pos, const std::string &x) override; + void write_value(size_t pos, const std::string &x, size_t size) override; + void write_value(size_t pos, const utils::blob &x) override; + void write_value(size_t pos, const utils::value &x, size_t size) override; +}; + +} + +#endif //TEST_PARAMETER_BINDER_HPP diff --git a/test/orm/backend/test_result_reader.cpp b/test/orm/backend/test_result_reader.cpp new file mode 100644 index 0000000..8498cff --- /dev/null +++ b/test/orm/backend/test_result_reader.cpp @@ -0,0 +1,92 @@ +#include "test_result_reader.hpp" + +#include "matador/utils/value.hpp" + +namespace matador::test::orm { + +size_t test_result_reader::column_count() const { + return 10; +} + +const char *test_result_reader::column(size_t index) const { + return ""; +} + +utils::result test_result_reader::fetch() { return utils::ok(rows_-- > 0); } + +size_t test_result_reader::start_column_index() const { + return 0; +} + +void test_result_reader::read_value(const char *id, const size_t index, int8_t &value) { + value = -8; +} + +void test_result_reader::read_value(const char *id, const size_t index, int16_t &value) { + value = -16; +} + +void test_result_reader::read_value(const char *id, const size_t index, int32_t &value) { + value = -32; +} + +void test_result_reader::read_value(const char *id, const size_t index, int64_t &value) { + value = -64; +} + +void test_result_reader::read_value(const char *id, const size_t index, uint8_t &value) { + value = 8; +} + +void test_result_reader::read_value(const char *id, const size_t index, uint16_t &value) { + value = 16; +} + +void test_result_reader::read_value(const char *id, const size_t index, uint32_t &value) { + value = 32; +} + +void test_result_reader::read_value(const char *id, const size_t index, uint64_t &value) { + value = 64; +} + +void test_result_reader::read_value(const char *id, const size_t index, bool &value) { + value = true; +} + +void test_result_reader::read_value(const char *id, const size_t index, float &value) { + value = 3.141572f; +} + +void test_result_reader::read_value(const char *id, const size_t index, double &value) { + value = 2.14159265358979323846; +} + +void test_result_reader::read_value(const char *id, const size_t index, matador::time &value) { +} + +void test_result_reader::read_value(const char *id, const size_t index, matador::date &value) { +} + +void test_result_reader::read_value(const char *id, const size_t index, char *value, const size_t size) { +} + +void test_result_reader::read_value(const char *id, const size_t index, std::string &value) { + value = "Lorem ipsum"; +} + +void test_result_reader::read_value(const char *id, const size_t index, std::string &value, const size_t size) { + value = "Hello world"; +} + +void test_result_reader::read_value(const char *id, const size_t index, utils::blob &value) { + value = {'b', 'l', 'o', 'b'}; +} + +void test_result_reader::read_value(const char *id, const size_t index, utils::value &val, const size_t size) { + val = "value"; +} +utils::attribute_reader &test_result_reader::result_binder() { + return query_result_reader::result_binder(); +} +} // namespace matador::test::orm diff --git a/test/orm/backend/test_result_reader.hpp b/test/orm/backend/test_result_reader.hpp new file mode 100644 index 0000000..09f1cdd --- /dev/null +++ b/test/orm/backend/test_result_reader.hpp @@ -0,0 +1,43 @@ +#ifndef TEST_RESULT_READER_HPP +#define TEST_RESULT_READER_HPP + +#include "matador/sql/interface/query_result_reader.hpp" + +namespace matador::test::orm { + +class test_result_reader final : public sql::query_result_reader { +public: + [[nodiscard]] size_t column_count() const override; + [[nodiscard]] const char *column(size_t index) const override; + [[nodiscard]] utils::result fetch() override; + [[nodiscard]] size_t start_column_index() const override; + + void read_value(const char *id, size_t index, int8_t &value) override; + void read_value(const char *id, size_t index, int16_t &value) override; + void read_value(const char *id, size_t index, int32_t &value) override; + void read_value(const char *id, size_t index, int64_t &value) override; + void read_value(const char *id, size_t index, uint8_t &value) override; + void read_value(const char *id, size_t index, uint16_t &value) override; + void read_value(const char *id, size_t index, uint32_t &value) override; + void read_value(const char *id, size_t index, uint64_t &value) override; + void read_value(const char *id, size_t index, bool &value) override; + void read_value(const char *id, size_t index, float &value) override; + void read_value(const char *id, size_t index, double &value) override; + void read_value(const char *id, size_t index, matador::time &value) override; + void read_value(const char *id, size_t index, matador::date &value) override; + void read_value(const char *id, size_t index, char *value, size_t size) override; + void read_value(const char *id, size_t index, std::string &value) override; + void read_value(const char *id, size_t index, std::string &value, size_t size) override; + void read_value(const char *id, size_t index, utils::blob &value) override; + void read_value(const char *id, size_t index, utils::value &val, size_t size) override; + +protected: + attribute_reader &result_binder() override; + +private: + uint8_t rows_{5}; +}; + +} + +#endif //TEST_RESULT_READER_HPP diff --git a/test/orm/backend/test_statement.cpp b/test/orm/backend/test_statement.cpp new file mode 100644 index 0000000..47fac0d --- /dev/null +++ b/test/orm/backend/test_statement.cpp @@ -0,0 +1,22 @@ +#include "test_statement.hpp" +#include "test_result_reader.hpp" + +namespace matador::test::orm { +test_statement::test_statement(const sql::query_context &query) +: statement_impl(query) {} + +utils::result test_statement::execute() { + return utils::ok(static_cast(8)); +} + +utils::result, utils::error> test_statement::fetch() { + return utils::ok(std::make_unique(std::make_unique(), query_.prototype, query_.prototype.size())); +} + +void test_statement::reset() {} + +utils::attribute_writer &test_statement::binder() { + return binder_; +} + +} // namespace matador::test::orm diff --git a/test/orm/backend/test_statement.hpp b/test/orm/backend/test_statement.hpp new file mode 100644 index 0000000..ef93cc4 --- /dev/null +++ b/test/orm/backend/test_statement.hpp @@ -0,0 +1,26 @@ +#ifndef TEST_STATEMENT_HPP +#define TEST_STATEMENT_HPP + +#include "test_parameter_binder.hpp" + +#include "matador/sql/interface/statement_impl.hpp" + +namespace matador::test::orm { + +class test_statement final : public sql::statement_impl { +public: + explicit test_statement(const sql::query_context &query); + utils::result execute() override; + utils::result, utils::error> fetch() override; + void reset() override; + +protected: + utils::attribute_writer &binder() override; + +private: + test_parameter_binder binder_; +}; + +} + +#endif //TEST_STATEMENT_HPP diff --git a/test/orm/query/ConditionTests.cpp b/test/orm/query/ConditionTests.cpp new file mode 100644 index 0000000..a52f920 --- /dev/null +++ b/test/orm/query/ConditionTests.cpp @@ -0,0 +1,112 @@ +#include + +#include "matador/query/condition.hpp" + +#include "matador/sql/dialect_builder.hpp" + +using namespace matador::sql; +using namespace matador::query; + +class ConditionFixture { +protected: + dialect dlc = dialect_builder::builder() + .create() + .build(); + query_context ctx; +}; + +TEST_CASE_METHOD(ConditionFixture, "Test column user defined literal", "[column][literal]") { + const auto col = "name"_col; + + REQUIRE(col.name == "name"); +} + +TEST_CASE_METHOD(ConditionFixture, "Test logical condition", "[condition][logical]") { + const auto name_col = "name"_col; + + REQUIRE(name_col.name == "name"); + + auto cond1 = name_col != "george"; + + auto clause = cond1.evaluate(dlc, ctx); + REQUIRE(clause == "\"name\" <> 'george'"); + + auto cond2 = "age"_col != 9; + clause = cond2.evaluate(dlc, ctx); + REQUIRE(clause == "\"age\" <> 9"); +} + +TEST_CASE_METHOD(ConditionFixture, "Test and condition", "[condition][bin][and]") { + const auto name_col = "name"_col; + + REQUIRE(name_col.name == "name"); + + const auto cond = name_col == "Hans" && name_col == "Dieter"; + + auto clause = cond.evaluate(dlc, ctx); + REQUIRE(clause == "(\"name\" = 'Hans' AND \"name\" = 'Dieter')"); +} + +TEST_CASE_METHOD(ConditionFixture, "Test or condition", "[condition][bin][or]") { + const auto name_col = "name"_col; + + REQUIRE(name_col.name == "name"); + + const auto cond = name_col == "Hans" || name_col == "Dieter"; + + auto clause = cond.evaluate(dlc, ctx); + REQUIRE(clause == "\"name\" = 'Hans' OR \"name\" = 'Dieter'"); +} + +TEST_CASE_METHOD(ConditionFixture, "Test not condition", "[condition][not]") { + const auto name_col = "name"_col; + + REQUIRE(name_col.name == "name"); + + const auto cond = !(name_col != "Hans"); + + auto clause = cond.evaluate(dlc, ctx); + REQUIRE(clause == "NOT (\"name\" <> 'Hans')"); +} + +TEST_CASE_METHOD(ConditionFixture, "Test in condition", "[condition][in]") { + const auto age_col = "age"_col; + + REQUIRE(age_col.name == "age"); + auto cond = age_col != 7 && in(age_col, {7,5,5,8}); + + auto clause = cond.evaluate(dlc, ctx); + REQUIRE(clause == "(\"age\" <> 7 AND \"age\" IN (7, 5, 5, 8))"); + + cond = age_col != 7 && in(age_col, {7}); + clause = cond.evaluate(dlc, ctx); + REQUIRE(clause == "(\"age\" <> 7 AND \"age\" IN (7))"); +} + +TEST_CASE_METHOD(ConditionFixture, "Test in query condition", "[condition][in query]") { + const auto age_col = "age"_col; + const auto name_col = "name"_col; + + query_context sub_ctx; + sub_ctx.sql = R"(SELECT "name" FROM "test")"; + + auto cond = age_col != 7 && in(name_col, std::move(sub_ctx)); + auto clause = cond.evaluate(dlc, ctx); + REQUIRE(clause == "(\"age\" <> 7 AND \"name\" IN (SELECT \"name\" FROM \"test\"))"); +} + +TEST_CASE_METHOD(ConditionFixture, "Test between condition", "[condition][between]") { + const auto age_col = "age"_col; + + auto cond = age_col != 7 && between(age_col, 21, 30); + auto clause = cond.evaluate(dlc, ctx); + REQUIRE(clause == "(\"age\" <> 7 AND \"age\" BETWEEN 21 AND 30)"); +} + +TEST_CASE_METHOD(ConditionFixture, "Test like condition", "[condition][like]") { + const auto name_col = "name"_col; + + auto cond = like(name_col, "%er%"); + auto clause = cond.evaluate(dlc, ctx); + REQUIRE(clause == "\"name\" LIKE '%er%'"); +} diff --git a/test/orm/query/QueryBuilderTest.cpp b/test/orm/query/QueryBuilderTest.cpp new file mode 100644 index 0000000..b9fcc4b --- /dev/null +++ b/test/orm/query/QueryBuilderTest.cpp @@ -0,0 +1,235 @@ +#include + +#include "QueryFixture.hpp" + +#include +#include + +#include +#include +#include + +#include + +// #include "models/author.hpp" +// #include "models/book.hpp" + +using namespace matador::test; +using namespace matador::sql; +using namespace matador::query; +using namespace matador::utils; + +TEST_CASE_METHOD(QueryFixture, "Test create table sql statement string", "[query]") { + auto result = query::create() + .table({"person"}, { + make_pk_column("id"), + make_column("name", 255), + make_column("age") + }).str(*db); + + REQUIRE(result == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL, "age" INTEGER NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id)))##"); + + result = query::create() + .table("person", { + make_pk_column("id"), + make_column("name", {255, constraints::UNIQUE}, null_option::NOT_NULL), + make_column("age"), + make_fk_column("address", "address", "id") + }).str(*db); + + REQUIRE(result == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL UNIQUE, "age" INTEGER NOT NULL, "address" BIGINT NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id), CONSTRAINT FK_person_address FOREIGN KEY (address) REFERENCES address(id)))##"); +} + +TEST_CASE_METHOD(QueryFixture, "Test drop table sql statement string", "[query]") { + const auto result = query::drop() + .table("person") + .str(*db); + + REQUIRE(result == R"(DROP TABLE "person")"); +} + +TEST_CASE_METHOD(QueryFixture, "Test select sql statement string", "[query]") { + const auto result = query::select({"id", "name", "age"}) + .from("person") + .str(*db); + + REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person")"); +} + +TEST_CASE_METHOD(QueryFixture, "Test insert sql statement string", "[query]") { + const auto result = query::insert() + .into("person", { + "id", "name", "age" + }) + .values({7UL, "george", 65U}) + .str(*db); + + REQUIRE(result == R"(INSERT INTO "person" ("id", "name", "age") VALUES (7, 'george', 65))"); +} + +TEST_CASE_METHOD(QueryFixture, "Test update sql statement string", "[query]") { + auto result = query::update("person") + .set({ + {"id", 7UL}, + {"name", "george"}, + {"age", 65U} + }) + .str(*db); + + REQUIRE(result == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65)"); + + result = query::update("person") + .set({ + {"id", 7UL}, + {"name", "george"}, + {"age", 65U} + }) + .where("id"_col > 9) + .order_by("id").asc() + .limit(3) + .offset(2) + .str(*db); + + REQUIRE(result == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65 WHERE "id" > 9 ORDER BY "id" ASC LIMIT 3 OFFSET 2)"); +} + +TEST_CASE_METHOD(QueryFixture, "Test update limit sql statement", "[query][update][limit]") { + const auto result = query::update("person") + .set({{"id", 7UL}, {"name", "george"}, {"age", 65U}}) + .where("name"_col == "george") + .order_by("id"_col).asc() + .limit(2) + .str(*db); + + REQUIRE(result == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65 WHERE "name" = 'george' ORDER BY "id" ASC LIMIT 2)"); +} + +TEST_CASE_METHOD(QueryFixture, "Test delete sql statement string", "[query]") { + const auto result = query::remove().from("person").str(*db); + + REQUIRE(result == R"(DELETE FROM "person")"); +} + +TEST_CASE_METHOD(QueryFixture, "Test delete limit sql statement", "[query][delete][limit]") { + const auto result = query::remove() + .from("person") + .where("name"_col == "george") + .order_by("id"_col).asc() + .limit(2) + .str(*db); + + REQUIRE(result == R"(DELETE FROM "person" WHERE "name" = 'george' ORDER BY "id" ASC LIMIT 2)"); +} + +TEST_CASE_METHOD(QueryFixture, "Test select sql statement string with where clause", "[query]") { + auto result = query::select({"id", "name", "age"}) + .from("person") + .where("id"_col == 8 && "age"_col > 50) + .str(*db); + + REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = 8 AND "age" > 50))"); + + result = query::select({"id", "name", "age"}) + .from("person") + .where("id"_col == _ && "age"_col > 50) + .str(*db); + + REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = ? AND "age" > 50))"); +} + +TEST_CASE_METHOD(QueryFixture, "Test insert sql statement with placeholder", "[query]") { + auto result = query::insert() + .into("person", {"id", "name", "age"}) + .values({_, _, _}) + .str(*db); + + REQUIRE(result == R"(INSERT INTO "person" ("id", "name", "age") VALUES (?, ?, ?))"); + + result = query::insert() + .into("person", {"id", "name", "age"}) + .values({9, "george", _}) + .str(*db); + + REQUIRE(result == R"(INSERT INTO "person" ("id", "name", "age") VALUES (9, 'george', ?))"); +} + +TEST_CASE_METHOD(QueryFixture, "Test select sql statement string with order by", "[query]") { + const auto result = query::select({"id", "name", "age"}) + .from("person") + .order_by("name"_col).asc() + .str(*db); + + REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person" ORDER BY "name" ASC)"); +} + +TEST_CASE_METHOD(QueryFixture, "Test select sql statement string with group by", "[query]") { + const auto result = query::select({"id", "name", "age"}) + .from("person") + .group_by("age"_col) + .str(*db); + + REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person" GROUP BY "age")"); +} + +TEST_CASE_METHOD(QueryFixture, "Test select sql statement string with offset and limit", "[query]") { + const auto result = query::select({"id", "name", "age"}) + .from("person") + .order_by("id"_col).asc() + .limit(20) + .offset(10) + .str(*db); + + REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person" ORDER BY "id" ASC LIMIT 20 OFFSET 10)"); +} + +TEST_CASE_METHOD(QueryFixture, "Test create, insert and select a blob column", "[query][blob]") { + auto result = query::create() + .table("person", { + make_pk_column("id"), + make_column("name", 255), + make_column("data") + }) + .str(*db); + + REQUIRE(result == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL, "data" BLOB NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id)))##"); + + result = query::insert() + .into("person", {"id", "name", "data"}) + .values({7UL, "george", blob{1, 'A', 3, 4}}) + .str(*db); + + REQUIRE(result == R"(INSERT INTO "person" ("id", "name", "data") VALUES (7, 'george', X'01410304'))"); + + result = query::select({"id", "name", "data"}) + .from("person") + .str(*db); + + REQUIRE(result == R"(SELECT "id", "name", "data" FROM "person")"); +} + +TEST_CASE_METHOD(QueryFixture, "Test select statement with join_left", "[query][statement][join_left]") { + const auto result = query::select({"f.id", "ap.brand", "f.pilot_name"}) + .from({"flight", "f"}) + .join_left({"airplane", "ap"}) + .on("f.airplane_id"_col == "ap.id"_col) + .str(*db); + + REQUIRE(result == R"(SELECT "f"."id", "ap"."brand", "f"."pilot_name" FROM "flight" "f" LEFT JOIN "airplane" "ap" ON "f"."airplane_id" = "ap"."id")"); +} + +// TEST_CASE_METHOD(QueryFixture, "Select statement with aliased columns", "[query][select][alias]") { +// using namespace matador::test; +// connection noop("noop://noop.db"); +// schema scm("noop"); +// scm.attach("authors"); +// scm.attach("books"); +// +// +// const auto result = query::select(scm) +// .from("authors"_tab.as("T01")) +// .str(*db); +// +// const auto expected_sql = R"(SELECT "T01"."id", "T01"."first_name", "T01"."last_name", "T01"."date_of_birth", "T01"."year_of_birth", "T01"."distinguished" FROM "authors" "T01")"; +// +// REQUIRE(result == expected_sql); +// } \ No newline at end of file diff --git a/test/orm/query/QueryFixture.cpp b/test/orm/query/QueryFixture.cpp new file mode 100644 index 0000000..294b02e --- /dev/null +++ b/test/orm/query/QueryFixture.cpp @@ -0,0 +1,15 @@ +#include "QueryFixture.hpp" + +#include "../backend/test_backend_service.hpp" + +#include "matador/sql/interface/connection_impl.hpp" + +namespace matador::test { + +QueryFixture::QueryFixture() { + sql::backend_provider::instance().register_backend("noop", std::make_unique()); + + db = std::make_unique("noop://noop.db"); +} + +} \ No newline at end of file diff --git a/test/orm/query/QueryFixture.hpp b/test/orm/query/QueryFixture.hpp new file mode 100644 index 0000000..f3c9793 --- /dev/null +++ b/test/orm/query/QueryFixture.hpp @@ -0,0 +1,21 @@ +#ifndef QUERY_FIXTURE_HPP +#define QUERY_FIXTURE_HPP + +#include "matador/sql/connection.hpp" + +#include + +namespace matador::test { + +class QueryFixture { +public: + QueryFixture(); + ~QueryFixture() = default; + +protected: + std::unique_ptr db; +}; + +} + +#endif //QUERY_FIXTURE_HPP diff --git a/test/orm/query/QueryTest.cpp b/test/orm/query/QueryTest.cpp new file mode 100644 index 0000000..3651238 --- /dev/null +++ b/test/orm/query/QueryTest.cpp @@ -0,0 +1,36 @@ +#include + +#include + +#include "QueryFixture.hpp" + +using namespace matador::test; +using namespace matador::query; + +TEST_CASE_METHOD(QueryFixture, "Test simple query", "[query][fetch]") { + auto result = query::select({"id", "name", "age"}) + .from("person") + .fetch_all(*db); + + REQUIRE(result.is_ok()); + + for (const auto& row : *result) { + REQUIRE(row.size() == 3); + } + + auto single = query::select({"id", "name", "age"}) + .from("person") + .fetch_one(*db); + + REQUIRE(single.is_ok()); + REQUIRE(single.value().has_value()); + REQUIRE(single.value().value().size() == 3); + + auto val = query::select({"id", "name", "age"}) + .from("person") + .fetch_value(*db); + + REQUIRE(val.is_ok()); + REQUIRE(val.value().has_value()); + // REQUIRE(val.value().value() == -1); +} \ No newline at end of file diff --git a/test/ColumnTest.cpp b/test/orm/sql/ColumnTest.cpp similarity index 72% rename from test/ColumnTest.cpp rename to test/orm/sql/ColumnTest.cpp index fb14b48..341b31d 100644 --- a/test/ColumnTest.cpp +++ b/test/orm/sql/ColumnTest.cpp @@ -3,35 +3,36 @@ #include "matador/sql/column_definition.hpp" using namespace matador::sql; +using namespace matador::utils; -TEST_CASE("Create empty column", "[column]") { +TEST_CASE("Test create empty column", "[column]") { column_definition c("name"); REQUIRE(c.name() == "name"); REQUIRE(c.index() == -1); - REQUIRE(c.type() == data_type_t::type_unknown); + REQUIRE(c.type() == basic_type::type_null); REQUIRE(c.ref_table().empty()); REQUIRE(c.ref_column().empty()); c.set(std::string{"george"}, 255); - REQUIRE(c.type() == data_type_t::type_varchar); + REQUIRE(c.type() == basic_type::type_varchar); REQUIRE(c.as() == "george"); c.set(7); - REQUIRE(c.type() == data_type_t::type_int); + REQUIRE(c.type() == basic_type::type_int32); REQUIRE(c.as() == "7"); REQUIRE(c.as() == 7); REQUIRE(c.str() == "7"); } -TEST_CASE("Copy and move column", "[column]") { +TEST_CASE("Test copy and move column", "[column]") { column_definition c("name"); c.set(std::string{"george"}, 255); REQUIRE(c.name() == "name"); REQUIRE(c.index() == -1); REQUIRE(c.ref_table().empty()); REQUIRE(c.ref_column().empty()); - REQUIRE(c.type() == data_type_t::type_varchar); + REQUIRE(c.type() == basic_type::type_varchar); REQUIRE(c.as() == "george"); REQUIRE(c.attributes().size() == 255); @@ -40,7 +41,7 @@ TEST_CASE("Copy and move column", "[column]") { REQUIRE(c2.index() == -1); REQUIRE(c2.ref_table().empty()); REQUIRE(c2.ref_column().empty()); - REQUIRE(c2.type() == data_type_t::type_varchar); + REQUIRE(c2.type() == basic_type::type_varchar); REQUIRE(c2.as() == "george"); REQUIRE(c2.attributes().size() == 255); @@ -49,7 +50,7 @@ TEST_CASE("Copy and move column", "[column]") { REQUIRE(c3.index() == -1); REQUIRE(c3.ref_table().empty()); REQUIRE(c3.ref_column().empty()); - REQUIRE(c3.type() == data_type_t::type_varchar); + REQUIRE(c3.type() == basic_type::type_varchar); REQUIRE(c3.as() == "george"); REQUIRE(c3.attributes().size() == 255); @@ -57,7 +58,7 @@ TEST_CASE("Copy and move column", "[column]") { REQUIRE(c2.index() == -1); REQUIRE(c2.ref_table().empty()); REQUIRE(c2.ref_column().empty()); - REQUIRE(c2.type() == data_type_t::type_varchar); - REQUIRE(c2.as().empty()); + REQUIRE(c2.type() == basic_type::type_null); + // REQUIRE(!c2.as().has_value()); REQUIRE(c2.attributes().size() == 255); } \ No newline at end of file diff --git a/test/FieldTest.cpp b/test/orm/sql/FieldTest.cpp similarity index 91% rename from test/FieldTest.cpp rename to test/orm/sql/FieldTest.cpp index bb0b4bb..ffee8d3 100644 --- a/test/FieldTest.cpp +++ b/test/orm/sql/FieldTest.cpp @@ -4,12 +4,11 @@ using namespace matador; -TEST_CASE("Field test", "[field]") { +TEST_CASE("Test field", "[field]") { sql::field f("name"); REQUIRE(f.name() == "name"); REQUIRE(f.index() == -1); - REQUIRE(!f.is_unknown()); REQUIRE(f.is_null()); REQUIRE(!f.is_integer()); REQUIRE(!f.is_floating_point()); @@ -51,5 +50,5 @@ TEST_CASE("Field test", "[field]") { REQUIRE(blob_val.has_value()); REQUIRE(blob_val.value() == utils::blob{ 7,8,6,5,4,3 }); - REQUIRE_THROWS_AS(f.as(), std::logic_error); + REQUIRE(!f.as().has_value()); } \ No newline at end of file diff --git a/test/utils/auto_reset_event.cpp b/test/utils/auto_reset_event.cpp deleted file mode 100644 index aa4fd76..0000000 --- a/test/utils/auto_reset_event.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "auto_reset_event.hpp" - -namespace matador::test::utils { - -auto_reset_event::auto_reset_event() : state(false) {} - -void auto_reset_event::wait_one() -{ - std::unique_lock lock(sync); - underlying.wait(lock, [this](){return state.load();}); - state = false; -} - -void auto_reset_event::set() -{ - std::unique_lock lock(sync); - state = true; - underlying.notify_one(); -} - -void auto_reset_event::reset() -{ - std::unique_lock lock(sync); - state = false; -} -} \ No newline at end of file diff --git a/test/utils/auto_reset_event.hpp b/test/utils/auto_reset_event.hpp deleted file mode 100644 index cd1d25d..0000000 --- a/test/utils/auto_reset_event.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef QUERY_AUTO_RESET_EVENT_HPP -#define QUERY_AUTO_RESET_EVENT_HPP - -#include -#include -#include - -namespace matador::test::utils { - -class auto_reset_event -{ -public: - auto_reset_event(); - auto_reset_event(const auto_reset_event& other) = delete; - - void wait_one(); - void set(); - void reset(); - -private: - std::condition_variable underlying; - std::mutex sync; - std::atomic state; -}; - - -} - -#endif //QUERY_AUTO_RESET_EVENT_HPP