Compare commits

..
Author SHA1 Message Date
sascha 24677d61df Merge remote-tracking branch 'origin/feature/matador-ng' into feature/matador-ng 2026-07-31 17:52:25 +02:00
sascha 5da70ba3a5 refactored object_proxy and object_ptr and fixed their bugs 2026-07-30 12:34:02 +02:00
sascha c880728093 refactored result that an overload for void doesn't need to exist 2026-07-30 12:33:24 +02:00
sascha 78b30f9742 removed double line 2026-07-30 12:32:19 +02:00
sascha b19d0fd3ca improved version::str() and version::from_string() 2026-07-28 09:53:25 +02:00
sascha 730ab05213 small result fixes 2026-07-28 09:52:49 +02:00
sascha 6ecda781f5 collection_proxy progress 2026-07-20 07:02:16 +02:00
sascha d4865e4d10 collection proxy progress 2026-07-07 08:23:43 +02:00
sascha 06f6166f05 Enhanced some test descriptions 2026-07-06 16:06:09 +02:00
sascha 89b795c488 fixed compilation 2026-07-06 06:53:11 +02:00
sascha 1b98eb4019 collection_proxy progress added test frame 2026-06-26 15:52:53 +02:00
sascha 47fd6d68e4 Merge remote-tracking branch 'origin/feature/matador-ng' into feature/matador-ng 2026-06-15 08:43:20 +02:00
sascha 34a5cfcc88 started refactoring collection proxy 2026-06-15 08:42:58 +02:00
sascha 705d361aaa fixed restriction and constraint class to use size_t 2026-06-03 16:36:51 +02:00
sascha 87bd2f8b89 Revert "fixed compile"
This reverts commit 8914c06833.
2026-06-03 11:19:24 +02:00
sascha 8914c06833 fixed compile 2026-06-03 11:06:47 +02:00
sascha 34a9a8aa4a fixed table constructor with join and inverse join columns 2026-06-02 16:43:31 +02:00
sascha 12f8590634 delete many to many progress 2026-06-01 16:44:07 +02:00
sascha cea7b97f2b added join_column_index and inverse_join_column_index to object and table classes 2026-06-01 06:57:38 +02:00
sascha bd06034ebf renamed binary_operator to be pascal case 2026-05-29 15:09:11 +02:00
sascha 68d67b17b7 delete has many to many progress 2026-05-28 15:45:46 +02:00
sascha 73bd6f641c delete has many to many progress 2026-05-27 16:10:26 +02:00
sascha 98d58d8e60 small fixes 2026-05-27 16:10:03 +02:00
sascha b22a830d18 added SessionDeleteHasMany and SessionDeleteHasManyToMany tests 2026-05-26 15:50:32 +02:00
sascha 85e6ee4b93 fixed delete_query_builder.hpp 2026-05-26 15:50:03 +02:00
sascha 2e1b583741 moved recipe and ingredient pk generators into recipe.hpp 2026-05-26 15:49:49 +02:00
sascha 6701031007 removed unused include 2026-05-26 15:49:13 +02:00
sascha 57f57956de added delete_query_builder (progress) 2026-05-26 07:01:53 +02:00
sascha 141d798a41 small fixes and renames 2026-05-23 20:48:21 +02:00
sascha cc0bcbeb61 fixed some includes 2026-05-23 20:23:29 +02:00
sascha 5ec7c13420 integrated object cache into session and added message bus to object_cache and update tests 2026-05-22 15:26:16 +02:00
sascha ddf19bbf07 updated return type 2026-05-21 12:39:25 +02:00
sascha 194e139e8b changed passed error in or_else to release_error 2026-05-21 12:39:10 +02:00
sascha 6629a71f6e use existing result error in query_builder_exception constructor 2026-05-21 12:38:46 +02:00
sascha 72019bc1e7 fixed insert_step_processor 2026-05-21 12:04:10 +02:00
sascha 7216f07c9f refactored insert_query_builder into insert_step_processor and added insert_context 2026-05-20 16:22:15 +02:00
sascha aa8da1f76f insert_query_builder optimizations 2026-05-19 15:50:39 +02:00
sascha 627af1d1a8 removed unused code 2026-05-18 22:04:09 +02:00
sascha 0f3028c5c7 use specific type for vector to match expected sizes 2026-05-18 19:25:20 +02:00
sascha 25405de2e0 renamed record_printer to RecordPrinter 2026-05-18 19:25:04 +02:00
sascha e58b7b9b0d moved prepare_* methods from dialect to query_utils 2026-05-18 15:05:00 +02:00
sascha 052ff657c1 updated todo.md 2026-05-18 14:07:20 +02:00
sascha 08a28b1627 completed session tests for lazy and eager has_many_to_many tests 2026-05-18 14:07:08 +02:00
sascha 60f03246dc rename record_printer to RecordPrinter 2026-05-18 13:48:34 +02:00
sascha caacdfa34a added has_one eager functionality and tests 2026-05-18 12:28:21 +02:00
sascha 8c8423bf64 fixed has one definitions 2026-05-18 09:03:41 +02:00
sascha 820b5432b2 renamed collection resolver producer related classes 2026-05-18 09:03:24 +02:00
sascha bf99642af4 resolve has one progress 2026-05-18 06:58:26 +02:00
sascha 9f54716df2 resolve has one progress 2026-05-17 21:46:43 +02:00
sascha 57703dfb67 has one progress 2026-05-15 15:22:59 +02:00
sascha 22f6f71412 insert has one progress 2026-05-15 07:24:48 +02:00
sascha 1cdd83cb31 added belongs to and has one tests 2026-05-13 16:09:35 +02:00
sascha e3618c60e3 added more has many to many test cases with different pk generators 2026-05-12 16:04:11 +02:00
sascha ceb7795d9c has many primitive collection resolver with table and sequence 2026-05-11 21:17:27 +02:00
sascha 68eb2b6a6e has many primitive collection resolver 2026-05-11 20:54:48 +02:00
sascha a714cd2a53 has many primitive relation progress 2026-05-08 15:53:52 +02:00
sascha 1e08996087 renamed a member variable 2026-05-07 16:18:08 +02:00
sascha 5e6ba3666e added benchmark test suite 2026-05-07 14:29:35 +02:00
sascha 3d4a61749b renamed template parameters 2026-05-07 14:28:22 +02:00
sascha 3658664eb9 added checks to SessionInsertHasMany Test 2026-05-07 10:21:29 +02:00
sascha 8753042b3c added is_state method to object_proxy and object_ptr to check state 2026-05-07 10:06:38 +02:00
sascha 85d297995d insert query builder insert step improvements 2026-05-07 09:27:21 +02:00
sascha e6adffc264 insert query builder insert step refactoring progress 2026-05-06 22:51:16 +02:00
sascha 95cd363097 Merge remote-tracking branch 'origin/feature/matador-ng' into feature/matador-ng 2026-05-06 16:18:39 +02:00
sascha 9c37e989ba insert query builder improvements progress 2026-05-06 16:18:27 +02:00
sascha e7523e2bba updated catch2 2026-05-02 18:59:56 +02:00
sascha 37855dc6fb fixed sequence table 2026-04-30 15:49:54 +02:00
sascha 433b0e40e1 sequence table progress 2026-04-29 16:08:53 +02:00
sascha 3fb23e167e fixed pk sequence tests 2026-04-29 14:07:17 +02:00
sascha 8e1dbe2ccd moved create and drop methods to basic_schema 2026-04-29 12:57:44 +02:00
sascha 96c6c78cb8 has many generator progress 2026-04-28 16:43:01 +02:00
sascha 1a22c70320 pk generator progress 2026-04-26 22:51:52 +02:00
sascha 75d5cba6c3 session progress 2026-04-26 15:50:11 +02:00
sascha 656d045ccf fixing postgres tests progress 2026-04-24 16:56:44 +02:00
sascha 3a82f4891a moved session to namespace query and move basic_schema::initialize to session constructor 2026-04-23 16:01:44 +02:00
sascha a34a3dc266 removed unused methods from session 2026-04-22 14:09:39 +02:00
sascha b6456356b4 session and insert query builder progress 2026-04-22 14:04:36 +02:00
sascha 9b25f7f556 introduced field_attributes shortcuts and added method on_base() to all processor classes 2026-04-22 11:03:33 +02:00
sascha 78eb5d04cd session and insert query builder has many to many progress 2026-04-22 09:15:57 +02:00
sascha 59e1533cca insert query builder has many to many progress 2026-04-20 19:57:18 +02:00
sascha f144b26dbb session and insert query builder progress 2026-04-17 15:16:49 +02:00
sascha d734d647ed session and insert query builder progress 2026-04-16 16:53:06 +02:00
sascha 0a0e3752ea insert_query_builder progress 2026-04-16 07:00:37 +02:00
sascha a0326632ab insert_query_builder progress 2026-04-12 22:07:24 +02:00
sascha f03664d60a fix: comment out unused/invalid code 2026-04-10 16:33:35 +02:00
sascha 53ac6810d9 fix: pass object to pk_accessor 2026-04-10 16:33:06 +02:00
sascha f62f3e626b renamed initialize_executor to initialize 2026-04-10 16:32:37 +02:00
sascha d59b41d06c rename compile to build and added sql_hash to query_context 2026-04-06 11:52:55 +02:00
sascha 57246d8742 replaced pk_is_unset, pk_setter and pk_value_extractor with pk_accessor 2026-04-05 12:49:18 +02:00
sascha 1806e2670c added assignment from value, conversion to database_type and appropriate tests 2026-04-05 12:48:13 +02:00
sascha 9e4660c4ee removed unused on_attribute overloads 2026-04-03 19:46:53 +02:00
sascha f4e984a288 insert_query_builder has_many progress 2026-04-03 19:15:15 +02:00
sascha 855c9295fb addd const to variable 2026-04-03 19:15:01 +02:00
sascha 40dfc30dae fixed indentation for uuid class 2026-04-03 19:14:38 +02:00
sascha 920fdc68ed refactored identifier, added tests and added primary_key_accessor and tests 2026-04-03 19:14:14 +02:00
sascha f751541bdd added missing test in CMakeLists.txt 2026-03-27 15:10:55 +01:00
sascha 833cb64352 added test for session insert 2026-03-27 15:10:03 +01:00
sascha 597965b62f manual_pk_generator.cpp progress 2026-03-27 07:06:01 +01:00
sascha fedce38270 removed class query and moved their static methods to simple functions 2026-03-26 09:34:32 +01:00
sascha 3fc08fb7ff added tests for sequence generator and table sequence pk generator 2026-03-25 15:56:57 +01:00
sascha ee8ce62687 added some todo information to clarify insert_query_builder functionality 2026-03-25 15:56:22 +01:00
sascha 905ee019d7 startet pk_accessor class 2026-03-19 16:46:44 +01:00
sascha 907bcf90a6 some code formatting 2026-03-19 11:54:50 +01:00
sascha 6b2db17f58 added test for table sequence 2026-03-19 11:52:34 +01:00
sascha 6423bd505b pk generator progress and sequence test progress 2026-03-18 15:54:08 +01:00
sascha 7996608f62 added missing nodiscard 2026-03-16 13:37:33 +01:00
sascha 53b10fd1f9 sequence progress 2026-03-15 22:33:35 +01:00
sascha 86c9e6b1cd sequence tests progress 2026-03-13 16:18:41 +01:00
sascha 6bdf281eab used make_object for object_ptr in tests 2026-03-08 20:24:34 +01:00
sascha 801fbdd187 fixed nextval and update tests 2026-03-07 16:34:48 +01:00
sascha 2aff0cabbb column_expression progress 2026-03-06 15:39:53 +01:00
sascha 9b4738a5ff progress on column_expression and table_column_expression 2026-03-06 07:29:23 +01:00
sascha bc8158a6ca column_expression progress 2026-03-05 16:10:02 +01:00
sascha d9eaa2009f split column expression into separate classes 2026-03-02 16:20:54 +01:00
sascha 85c56584be added column_expression and table_column_expression 2026-03-01 20:23:59 +01:00
sascha 1e78430855 added returning to update statement and added error code implementation for query 2026-02-27 16:29:49 +01:00
sascha 626eae1ac8 added more pk generator types 2026-02-26 07:31:35 +01:00
sascha 0157233b02 removed serialize(const char*) method 2026-02-25 15:51:00 +01:00
sascha cab3e508bb Merge remote-tracking branch 'origin/feature/matador-ng' into feature/matador-ng 2026-02-24 16:24:18 +01:00
sascha d50a8f2b76 added sequence_pk_generator implementation 2026-02-24 16:24:06 +01:00
sascha e45c38b229 added object_cache and test 2026-02-22 22:36:42 +01:00
sascha a2c5bca709 has many to many relation inserts 2026-02-19 22:32:15 +01:00
sascha cfd2a9c423 some test adjustments 2026-02-19 22:31:51 +01:00
sascha 1deaab116c set result_type in query_context to specify type 2026-02-19 18:17:42 +01:00
sascha 02d6ac514c added checks for lazy relation 2026-02-19 18:17:15 +01:00
sascha 4377f2fab1 comment unused variables 2026-02-19 18:16:55 +01:00
sascha 6c3af53dfa added missing include for atomic 2026-02-18 22:17:38 +01:00
sascha 36168b2e33 some code refactoring and cleanup 2026-02-18 15:44:33 +01:00
sascha e5072741f0 insert_query_builder progress 2026-02-18 15:32:27 +01:00
sascha 0876535b44 primary key generator progress: added primary_key_generator_finder 2026-02-17 22:43:53 +01:00
sascha 38dc42ade8 insert_query_builder progress 2026-02-17 16:19:14 +01:00
sascha 5668f1060b added sequence-specific code (create, drop and nextval) 2026-02-17 13:31:06 +01:00
sascha 80426c5293 updated todo.md 2026-02-16 11:30:01 +01:00
sascha ac75bb6e3a added test for creating table with identity primary key 2026-02-16 11:29:47 +01:00
sascha ae7c1f510f introduced make_object function to create object_ptr instances and removed some session::insert overloads for simplicity 2026-02-16 11:29:27 +01:00
sascha a2a6448e03 introduced object_state enum in object_proxy 2026-02-16 11:28:49 +01:00
sascha f38be4c6d9 added returning token for insert query 2026-02-15 18:34:12 +01:00
sascha 9065355ee0 refactored record classe 2026-02-15 18:33:22 +01:00
Sascha Kühl 746fd9e8d2 added some additional methods to table for later use 2026-02-12 16:16:46 +01:00
Sascha Kühl 4c63322024 identity generator progress 2026-02-11 16:01:44 +01:00
Sascha Kühl 881e93e0f6 added markers for handling insert 2026-02-10 16:30:39 +01:00
Sascha Kühl 40b163ac39 pass query_context to execute 2026-02-10 13:17:05 +01:00
Sascha Kühl 99677b0e21 introduced execute_result 2026-02-10 11:57:09 +01:00
Sascha Kühl d9f9712487 added InsertQueryBuilderTest.cpp 2026-02-09 16:46:56 +01:00
Sascha Kühl c1620d620e compile fixes in session 2026-02-09 13:04:55 +01:00
Sascha Kühl cf977a32aa formatting amd naming issues in message_bus.hpp 2026-02-09 13:04:36 +01:00
sascha 54e6786ceb some improvements to select_query_builder and started insert_query_builder 2026-02-08 22:38:11 +01:00
sascha a498efd21d renamed query::entity_query_data to query::select_query_data 2026-02-07 10:37:03 +01:00
Sascha Kühl c0e0499204 small changes in log_manager.cpp and add is_database_primitive type traits and test 2026-02-05 16:36:27 +01:00
Sascha Kühl 018e27a22a renamed cascade_type enums 2026-02-04 15:18:46 +01:00
Sascha Kühl 11bba90ded reverted CMakeLists.txt 2026-02-04 15:15:19 +01:00
Sascha Kühl 1a1ecd1613 logger fixes 2026-02-04 15:13:27 +01:00
Sascha Kühl 520b2bab49 stabilized logger classes 2026-02-04 14:45:26 +01:00
Sascha Kühl e7e66f44e2 moved pk_reader to internal 2026-02-04 10:16:24 +01:00
Sascha Kühl 4d886f0343 collection has-many-to-many resolve progress 2026-02-03 15:36:58 +01:00
sascha 73abd61eba has many to many collection resolver progress 2026-02-03 07:26:37 +01:00
Sascha Kühl b2d5db2702 enabled use of sql_logger in statement.cpp 2026-02-02 16:13:18 +01:00
Sascha Kühl 621c3703fd collection_resolver progress 2026-02-02 13:40:53 +01:00
sascha f9333158e2 collection resolver progress 2026-02-01 13:17:40 +01:00
sascha c838e9dd7b removed query_record_result and moved content to query_result 2026-01-31 11:25:44 +01:00
Sascha Kühl 0d3ab09c14 collection_resolver progress 2026-01-30 15:53:31 +01:00
Sascha Kühl bcec740d60 collection_resolver progress 2026-01-29 17:23:16 +01:00
Sascha Kühl 602b77e67b collection_resolver progress 2026-01-28 16:09:54 +01:00
Sascha Kühl 6205ae81c9 collection_resolver progress 2026-01-27 16:31:37 +01:00
sascha b6cabbe3ed collection resolver progress 2026-01-27 08:14:05 +01:00
Sascha Kühl 1cf39d0ef1 collection_resolver progress 2026-01-26 16:20:16 +01:00
sascha 046c1d8ed2 collection resolver producer progress 2026-01-25 22:00:11 +01:00
sascha 64f2d6899a removed unused parameter in handle_column function 2026-01-25 11:35:05 +01:00
Sascha Kühl d69c0a5769 added work query example 2026-01-23 16:32:29 +01:00
Sascha Kühl 36a160e1c7 added table_column tests 2026-01-23 15:14:20 +01:00
Sascha Kühl 4b015c8ced fixed table_column naming consistency and added result_name method 2026-01-23 13:03:41 +01:00
Sascha Kühl 15f959b24e renamed sql_command enum names 2026-01-23 09:22:27 +01:00
sascha d13ccc861e renamed query_compiler to query_builder. Changed handling of column names (broken) 2026-01-23 08:10:38 +01:00
Sascha Kühl 686b1ddb7a progress on query compiler 2026-01-22 16:14:42 +01:00
Sascha Kühl b331ef6923 criteria progress 2026-01-22 11:38:02 +01:00
Sascha Kühl 328d164c05 criteria progress 2026-01-21 16:08:27 +01:00
sascha ac514c3765 query collection resolver producer progress 2026-01-20 08:12:21 +01:00
sascha 623a64b610 added collection, collection proxy and collection resolver implementations 2026-01-18 22:11:51 +01:00
Sascha Kühl 4c899ae2f2 added has_many for non object_ptr types 2026-01-16 15:54:21 +01:00
Sascha Kühl 5dae396842 use int64_t as id type in shipment model 2026-01-16 13:09:09 +01:00
Sascha Kühl bc1d5effd3 added compiler flags for windows to support /bigobj option 2026-01-16 13:08:50 +01:00
Sascha Kühl 340fa5fd28 cast explicitly to std::vector for query_insert_intermediate::into method 2026-01-16 13:08:19 +01:00
Sascha Kühl 1143710f8d added missing include of stringstream for windows 2026-01-16 13:07:49 +01:00
sascha 7626331866 implemented lazy loading for object_ptr and belongs_to 2026-01-15 12:40:54 +01:00
sascha db8e137c11 added belongs-to-eager test 2026-01-10 18:54:18 +01:00
sascha b9f5819be5 use table metas where possible 2026-01-09 18:10:23 +01:00
sascha ac53526a27 added SchemaTest, fixed bug in class shipment, added contains methods to class schema and enabled some tests 2026-01-09 09:32:47 +01:00
sascha 930b9d1aa4 added timestamp code, support columns for group by and order by and tested load has-many eager code 2026-01-08 15:14:31 +01:00
sascha 0bf945cd13 some improvements to dump schema 2026-01-07 09:32:57 +01:00
sascha c6747074f5 renamed timestamp to timestamp_type_t 2026-01-07 09:23:47 +01:00
sascha f30c250a20 renamed blob to blob_type_t 2026-01-07 09:21:10 +01:00
sascha 41f6c9f516 adjust work project to the current codebase 2026-01-07 09:09:44 +01:00
sascha dfd31cdf35 moved dump schema method to class schema 2026-01-07 09:09:17 +01:00
sascha 099b72be05 remove double sql command 2026-01-07 09:08:53 +01:00
sascha 9155927f89 added missing space 2026-01-07 09:08:23 +01:00
sascha f46d9b21a2 bugfix: use valid node for relation endpoint registration 2026-01-07 09:07:39 +01:00
sascha 456a410946 added type traits for timestamp 2026-01-07 09:07:05 +01:00
sascha c48bd4f9d6 added test for date and time, sql read and write functionality 2026-01-06 22:23:50 +01:00
sascha 0c6b5a0a93 added date_type_t, time_type_t and timestamp, ensure allocated postgres resources are released, split repository into basic_repository and repository. 2026-01-06 15:43:20 +01:00
sascha f07a360da2 reformatted postgres_connection.cpp 2026-01-01 17:11:25 +01:00
sascha a79d50e6e8 fixed PostgreSQL tests 2026-01-01 17:08:20 +01:00
sascha 7cb64515bd added meta table macro and removed unused query_macro 2025-12-31 13:54:32 +01:00
sascha e768497cfd fixed demo project 2025-12-31 13:06:17 +01:00
sascha b9d35cbd4e renamed column to table_column and constraint to table_constraint. 2025-12-31 13:01:54 +01:00
sascha ab2d39407c schema progress (does not compile) 2025-12-31 11:56:12 +01:00
Sascha Kühl d104222fdd observer progress, added to schema and added schema_observer class 2025-12-19 15:56:36 +01:00
Sascha Kühl 5e2d2ddde5 added observer to repository and updated tests 2025-12-18 16:13:36 +01:00
Sascha Kühl 6307850721 moved schema to query namespace 2025-12-17 15:15:04 +01:00
Sascha Kühl 9eec5b64fb fixed compilation 2025-12-16 16:21:09 +01:00
Sascha Kühl 4a19322ef8 use raw pointer of table in class column (progress, needs fixes in tests) 2025-12-15 17:31:43 +01:00
sascha 9418f28348 constraint progress 2025-12-14 20:50:00 +01:00
sascha 348f3a16d0 removed template type from foreign_node_completer::complete method 2025-12-13 10:56:25 +01:00
Sascha Kühl 9ba58eb05b constraint, column and table query progress 2025-12-12 15:49:30 +01:00
Sascha Kühl 758ea96ea4 reactivated relation_completer 2025-12-12 08:49:40 +01:00
Sascha Kühl 79c46bbd38 fixed object generation on nested objects 2025-12-11 15:41:45 +01:00
Sascha Kühl 571cfd52ae removed name member from restriction and changed attribute member to a reference 2025-12-11 09:26:35 +01:00
Sascha Kühl f842efb611 removed attribute_generator and constraints_generator 2025-12-11 09:13:14 +01:00
Sascha Kühl 5afb616205 object generation progress 2025-12-10 17:05:24 +01:00
sascha 9b7b74524f removed string from restriction 2025-12-09 21:13:47 +01:00
Sascha Kühl c4f8731262 renamed constraint to restriction 2025-12-09 14:53:29 +01:00
Sascha Kühl 4fcbe9c064 object changes 2025-12-09 14:47:08 +01:00
sascha d4a8801cd2 preparations for object class changes 2025-12-08 22:29:07 +01:00
sascha 108eca4e53 renamed basic_types enumerations 2025-12-08 20:04:04 +01:00
sascha d4ef97ef5a removed make_relation_node method from repository_node 2025-12-08 19:45:35 +01:00
Sascha Kühl 263c202c69 some column interface refactorings 2025-12-08 16:27:00 +01:00
Sascha Kühl 3b0b9615ca renamed fetch types to be pascal case 2025-12-08 13:44:53 +01:00
sascha 2fcb504b9a schema creation progress 2025-12-07 22:18:01 +01:00
Sascha Kühl 2e354b435c progress on combining foreign_node_completer and relation_completer 2025-12-05 15:38:35 +01:00
sascha 98da0884f1 schema creation progress 2025-12-05 07:32:59 +01:00
sascha d2f1bc0b8e Merge remote-tracking branch 'origin/feature/matador-ng' into feature/matador-ng
# Conflicts:
#	source/orm/query/query_compiler.cpp
2025-12-04 19:20:49 +01:00
Sascha Kühl 980cabe94f fixed schema creation 2025-12-04 16:06:39 +01:00
sascha bbe65f2a57 reverted CMakeLists.txt 2025-12-03 17:14:21 +01:00
sascha f6aa100435 fixed query builder test 2025-12-03 17:13:15 +01:00
sascha e74424aaaa added missing includes 2025-12-03 17:13:01 +01:00
Sascha Kühl 19eb54df8d query builder progress 2025-12-03 16:18:03 +01:00
Sascha Kühl bfbbd4c589 query builder progress 2025-12-02 16:19:13 +01:00
sascha c156ab5e74 object generation progress 2025-12-01 22:03:07 +01:00
Sascha Kühl 422df82b7c object generating progress 2025-12-01 16:14:13 +01:00
sascha 19b7044773 object generation 2025-11-30 22:03:48 +01:00
Sascha Kühl f47b3bb87f object generating progress 2025-11-25 16:27:25 +01:00
Sascha Kühl ab7a2db869 added pk information to class object 2025-11-25 08:54:58 +01:00
Sascha Kühl ede5a8c636 object generator progress 2025-11-24 16:26:52 +01:00
sascha e67c0f39a3 preparations for object_generator (generating objects with attributes and constraints) 2025-11-23 15:58:14 +01:00
sascha 19b0adf737 fixed compilation and tests 2025-11-23 11:02:17 +01:00
Sascha Kühl 78abf16f28 builder progress (attribute, object and constraint class in todo.md) 2025-11-21 15:58:33 +01:00
Sascha Kühl 34eaeca0bb small fixes for table, column and query_macro 2025-11-21 12:25:38 +01:00
Sascha Kühl e235c111a1 moved query macro generated class to namespace matador::query::meta 2025-11-21 10:12:54 +01:00
Sascha Kühl a146dce321 renamed class attribute_definition_generator to attribute_generator 2025-11-21 09:24:53 +01:00
Sascha Kühl 758284c2b3 renamed class attribute_definition to attribute 2025-11-21 09:22:00 +01:00
Sascha Kühl dae3c1645b removed object_definition class 2025-11-21 09:16:53 +01:00
Sascha Kühl d8903ed772 compilation fixes and disabled database type char[] 2025-11-21 09:11:14 +01:00
Sascha Kühl 3f3773dc71 fixed core- and orm-tests. postgres-tests needs some work 2025-11-20 15:35:40 +01:00
Sascha Kühl 95c555d03a compilation fixes 2025-11-19 16:17:25 +01:00
Sascha Kühl 9c42df3f83 added a todo list 2025-11-18 16:27:51 +01:00
Sascha Kühl fb4bc03da0 moved column and table class from namespace sql to query 2025-11-18 16:27:38 +01:00
Sascha Kühl 7242dc2612 attribute_definition and object_definition progress 2025-11-17 15:58:09 +01:00
Sascha Kühl 9ffcee1317 removed the value field from attribute_definition because it's a definition and should not hold a value. 2025-11-17 13:16:18 +01:00
Sascha Kühl d1731a7f15 some sql classes refactorings like column and table structs to classes 2025-11-14 16:16:07 +01:00
Sascha Kühl 34e6d41554 move the process method outside the airplane class to prove extrusive processing functionality. 2025-11-14 09:48:50 +01:00
Sascha Kühl 61796d7c42 reuse a call to process instead of duplicting code 2025-11-14 09:47:28 +01:00
Sascha Kühl 167e2ef382 added a timeout parameter to connection_pool::acquire and a connection_pool::try_acquire with connection id 2025-11-13 16:28:41 +01:00
Sascha Kühl d6de8da3c8 schema progress 2025-11-13 16:27:58 +01:00
sascha 0d08ad4179 column generator progress 2025-11-12 22:18:56 +01:00
Sascha Kühl 1c5b2b21b0 column, placeholder and column_value generator progress 2025-11-12 15:53:03 +01:00
Sascha Kühl 9e76203bc9 column, placeholder and column_value generator progress 2025-11-11 16:19:58 +01:00
sascha 1d84b112f1 column generator progress 2025-11-11 08:02:46 +01:00
Sascha Kühl b3bff2dd1b column, placeholder and column_value generator progress 2025-11-10 16:12:53 +01:00
Sascha Kühl 4b51ad21da added alter table command to fluent query builder 2025-11-05 11:05:03 +01:00
Sascha Kühl c87a4c29b4 added alter table command to fluent query builder (progress) 2025-11-04 16:24:43 +01:00
Sascha Kühl c3c7ea57a2 added alter table command to fluent query builder (progress) 2025-11-03 16:21:26 +01:00
Sascha Kühl 557abecf91 orm schema class progress added SchemaTest.cpp 2025-10-31 16:28:34 +01:00
Sascha Kühl 5632746eb6 orm schema class progress 2025-10-30 15:29:31 +01:00
Sascha Kühl 270c2922ff uses criteria for session::find methods 2025-10-30 14:54:39 +01:00
Sascha Kühl 44c50cf2af added name methods to column class 2025-10-29 16:08:59 +01:00
Sascha Kühl 56f307fd37 added missing bind variable 2025-10-28 16:35:56 +01:00
Sascha Kühl 15d6328ae9 fixed compilation of postgres tests 2025-10-28 13:15:26 +01:00
Sascha Kühl cb95b974fc fixed column literal 2025-10-28 13:14:50 +01:00
Sascha Kühl 93785a6bc2 removed condition classes, functions and operators 2025-10-27 16:25:12 +01:00
Sascha Kühl 6fa56b2019 removed condition classes, functions and operators 2025-10-27 16:24:51 +01:00
Sascha Kühl f29f642e72 Changed names of dialect tokens to PascalCase writing 2025-10-27 10:33:50 +01:00
sascha 69ce2c34cf added criteria_evaluator and extended all criteria classes with missing methods 2025-10-26 19:57:22 +01:00
sascha 5d53addc60 added new query criteria class set 2025-10-20 13:20:56 +02:00
Sascha Kühl 009498fae1 added type shortcuts in message_bus 2025-10-17 08:53:41 +02:00
Sascha Kühl 1d40c72519 implemented session::remove 2025-10-16 15:40:43 +02:00
Sascha Kühl d08bdb9554 added update_object_binder progress 2025-10-15 16:14:22 +02:00
Sascha Kühl d3483ea5c2 added update_object_binder progress 2025-10-14 15:05:25 +02:00
Sascha Kühl d0e2e4340e statement binding progress 2025-10-13 16:14:03 +02:00
sascha 24842f3313 session update progress 2025-10-13 07:07:00 +02:00
sascha 1cda78412b fixed session::update implementation 2025-10-12 11:04:28 +02:00
sascha 399fa9a157 changed type of on_revision value parameter to uint64_t 2025-10-12 11:03:28 +02:00
Sascha Kühl 1fd2066946 progress on session::update 2025-10-10 15:51:51 +02:00
Sascha Kühl 463647ba59 fixed session tests 2025-10-08 16:03:00 +02:00
sascha cb0f6322f4 removed obsolete output 2025-10-06 19:23:58 +02:00
sascha 4fd9a74ae8 session queries as statement progress 2025-10-06 07:31:16 +02:00
Sascha Kühl 5490e67ecf connection info in connection pool progress 2025-10-02 16:12:21 +02:00
Sascha Kühl 12905e3df3 fixed some repository-related tests 2025-10-01 15:39:58 +02:00
sascha 5d2d6d4a30 fixed some query record tests 2025-09-30 22:30:34 +02:00
sascha b40fcf0d67 initialized missing member 2025-09-28 22:26:57 +02:00
Sascha Kühl d7758e827d schema progress 2025-09-26 16:13:07 +02:00
Sascha Kühl 3ae2b003aa renamed schema to repository 2025-08-28 16:12:26 +02:00
Sascha Kühl cce7f2c099 proxy lazy resolve progress 2025-08-25 16:00:32 +02:00
Sascha Kühl a4b35b913e proxy lazy resolve progress 2025-08-22 16:40:29 +02:00
Sascha Kühl 180037c156 small cosmetic fixes 2025-08-22 16:22:18 +02:00
Sascha Kühl 71cb1a25db proxy lazy resolve progress 2025-08-22 16:13:00 +02:00
Sascha Kühl 839a214cef proxy lazy resolve progress 2025-08-21 16:04:40 +02:00
Sascha Kühl f1323accda proxy lazy resolve progress 2025-08-20 16:26:59 +02:00
Sascha Kühl 6b24a0e413 proxy lazy resolve progress 2025-08-19 18:07:27 +02:00
sascha 18d21eb1c6 lazy loading progress 2025-08-19 07:30:23 +02:00
Sascha Kühl 1370eafac4 proxy lazy resolve progress 2025-08-18 16:21:24 +02:00
sascha 466977b620 started lazy loading 2025-08-18 07:37:23 +02:00
Sascha Kühl 2fdbfc651e removed obsolete interface namespace 2025-08-14 15:48:46 +02:00
Sascha Kühl 843d125b99 added session context and implemented the executor interface for session 2025-08-14 15:48:21 +02:00
Sascha Kühl a32330ef5b little statement_cache test changes 2025-08-12 16:30:45 +02:00
sascha 6ac3f80887 added more metrics to statement_cache 2025-08-11 20:51:13 +02:00
Sascha Kühl 01d7179604 added message event handling 2025-08-11 16:59:48 +02:00
Sascha Kühl 789be1174b removed namespace interface 2025-08-11 16:59:16 +02:00
sascha 2e2fcd01b6 added more metrics to statement_cache 2025-08-11 07:30:16 +02:00
sascha 4f017ac1de extended statement_cache with message bus events 2025-08-10 22:12:13 +02:00
sascha 60cef7e938 added a simple message bus 2025-08-09 15:39:13 +02:00
500 changed files with 25034 additions and 10630 deletions
+2
View File
@@ -0,0 +1,2 @@
# Generated by CLion for Google
BasedOnStyle: Google
+1 -1
View File
@@ -30,7 +30,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CMAKE_CXX_FLAGS_RELEASE "${GCC_CLANG_COMMON_FLAGS_RELEASE}") set(CMAKE_CXX_FLAGS_RELEASE "${GCC_CLANG_COMMON_FLAGS_RELEASE}")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
message(STATUS "MSVC detected - Adding compiler flags") message(STATUS "MSVC detected - Adding compiler flags")
# set(CMAKE_CXX_FLAGS "/W3 /EHsc /bigobj") set(CMAKE_CXX_FLAGS "/W3 /EHsc /bigobj")
# set(CMAKE_CXX_FLAGS_DEBUG "/MDd /Od /Zi /D_DEBUG /DDEBUG") # set(CMAKE_CXX_FLAGS_DEBUG "/MDd /Od /Zi /D_DEBUG /DDEBUG")
# set(CMAKE_CXX_FLAGS_RELEASE "/O1 /DNDEBUG") # set(CMAKE_CXX_FLAGS_RELEASE "/O1 /DNDEBUG")
add_compile_options(/Zc:preprocessor) add_compile_options(/Zc:preprocessor)
@@ -31,24 +31,28 @@ public:
[[nodiscard]] utils::result<utils::version, utils::error> client_version() const override; [[nodiscard]] utils::result<utils::version, utils::error> client_version() const override;
[[nodiscard]] utils::result<utils::version, utils::error> server_version() const override; [[nodiscard]] utils::result<utils::version, utils::error> server_version() const override;
utils::result<size_t, utils::error> execute(const std::string &stmt) override; utils::result<sql::execute_result, utils::error> execute(const sql::query_context &context) override;
utils::result<std::unique_ptr<sql::statement_impl>, utils::error> prepare(const sql::query_context &context) override; utils::result<std::unique_ptr<sql::statement_impl>, utils::error> prepare(const sql::query_context &context) override;
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch(const sql::query_context &context) override; utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch(const sql::query_context &ctx) override;
utils::result<std::vector<object::attribute_definition>, utils::error> describe(const std::string& table) override; utils::result<std::vector<object::attribute>, utils::error> describe(const std::string& table) override;
utils::result<bool, utils::error> exists(const std::string &schema_name, const std::string &table_name) override; utils::result<bool, utils::error> exists(const std::string &schema_name, const std::string &table_name) override;
[[nodiscard]] std::string to_escaped_string( const utils::blob& value ) const override; utils::result<bool, utils::error> sequence_exists(const std::string &schema_name, const std::string &sequence_name) override;
[[nodiscard]] std::string to_escaped_string( const utils::blob_type_t& value ) const override;
private: private:
[[nodiscard]] static std::string generate_statement_name(const sql::query_context &query) ; [[nodiscard]] static std::string generate_statement_name(const sql::query_context &ctx) ;
[[nodiscard]] utils::result<sql::execute_result, utils::error> execute(const std::string &stmt) const;
private: private:
PGconn *conn_{nullptr}; PGconn *conn_{nullptr};
using string_to_int_map = std::unordered_map<std::string, unsigned long>; using hash_to_string_map = std::unordered_map<size_t, std::string>;
static string_to_int_map statement_name_map_; static hash_to_string_map statement_name_map_;
}; };
} }
@@ -7,7 +7,7 @@
namespace matador::backends::postgres { namespace matador::backends::postgres {
class postgres_parameter_binder final : public sql::interface::parameter_binder { class postgres_parameter_binder final : public sql::parameter_binder {
public: public:
struct bind_data { struct bind_data {
explicit bind_data(size_t size); explicit bind_data(size_t size);
@@ -31,13 +31,14 @@ public:
void write_value(size_t pos, const bool &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 float &x) override;
void write_value(size_t pos, const double &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 utils::date_type_t &x ) override;
void write_value(size_t pos, const date &x ) override; void write_value(size_t pos, const utils::time_type_t &x ) override;
void write_value(size_t pos, const utils::timestamp_type_t &x ) override;
void write_value(size_t pos, const char *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 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) override;
void write_value(size_t pos, const std::string &x, size_t size) 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::blob_type_t &x) override;
void write_value(size_t pos, const utils::value &x, size_t size) override; void write_value(size_t pos, const utils::value &x, size_t size) override;
[[nodiscard]] const bind_data& params() const; [[nodiscard]] const bind_data& params() const;
@@ -9,8 +9,7 @@ namespace matador::backends::postgres {
namespace detail { namespace detail {
class empty_binder final : public utils::attribute_reader class empty_binder final : public utils::attribute_reader {
{
public: public:
void read_value(const char *, size_t, int8_t &) override {} 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, int16_t &) override {}
@@ -23,19 +22,19 @@ public:
void read_value(const char *, size_t, bool &) 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, float &) override {}
void read_value(const char *, size_t, double &) 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, utils::date_type_t &) override {}
void read_value(const char *, size_t, date &) override {} void read_value(const char *, size_t, utils::time_type_t &) override {}
void read_value(const char *, size_t, utils::timestamp_type_t &) override {}
void read_value(const char *, size_t, char *, size_t) 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 &) override {}
void read_value(const char *, size_t, std::string &, size_t) 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::blob_type_t &) override {}
void read_value(const char *, size_t, utils::value &, size_t) override {} void read_value(const char *, size_t, utils::value &, size_t) override {}
}; };
} }
class postgres_result_reader final : public sql::query_result_reader class postgres_result_reader final : public sql::query_result_reader {
{
public: public:
explicit postgres_result_reader(PGresult *result); explicit postgres_result_reader(PGresult *result);
~postgres_result_reader() override; ~postgres_result_reader() override;
@@ -57,12 +56,13 @@ public:
void read_value(const char *id, size_t index, bool &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, float &value) override;
void read_value(const char *id, size_t index, double &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, utils::date_type_t &) override;
void read_value(const char *id, size_t index, matador::date &value) override; void read_value(const char *id, size_t index, utils::time_type_t &) override;
void read_value(const char *id, size_t index, utils::timestamp_type_t &) 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, 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) 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, 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::blob_type_t &value) override;
void read_value(const char *id, size_t index, utils::value &val, size_t size) override; void read_value(const char *id, size_t index, utils::value &val, size_t size) override;
protected: protected:
@@ -11,17 +11,18 @@ namespace matador::backends::postgres {
class postgres_statement final : public sql::statement_impl class postgres_statement final : public sql::statement_impl
{ {
public: public:
postgres_statement(PGconn *db, std::string name, const sql::query_context &query); postgres_statement(PGconn *db, PGresult *res, std::string name, const sql::query_context &query);
~postgres_statement() override;
utils::result<size_t, utils::error> execute(const sql::interface::parameter_binder& bindings) override; utils::result<sql::execute_result, utils::error> execute(const sql::parameter_binder& bindings) override;
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch(const sql::interface::parameter_binder& bindings) override; utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch(const sql::parameter_binder& bindings) override;
void reset() override;
protected: protected:
[[nodiscard]] std::unique_ptr<utils::attribute_writer> create_binder() const override; [[nodiscard]] std::unique_ptr<utils::attribute_writer> create_binder() const override;
private: private:
PGconn *db_{nullptr}; PGconn *db_{nullptr};
PGresult *res_ = nullptr;
std::string name_; std::string name_;
}; };
+159 -109
View File
@@ -3,21 +3,22 @@
#include "postgres_result_reader.hpp" #include "postgres_result_reader.hpp"
#include "postgres_statement.hpp" #include "postgres_statement.hpp"
#include "matador/object/attribute_definition.hpp" #include "matador/object/attribute.hpp"
#include "matador/sql/error_code.hpp" #include "matador/sql/error_code.hpp"
#include "matador/sql/record.hpp" #include "matador/sql/record.hpp"
#include "matador/sql/internal/query_result_impl.hpp" #include "matador/sql/internal/query_result_impl.hpp"
#include "matador/utils/string.hpp"
#include <algorithm> #include <algorithm>
#include <sstream> #include <sstream>
namespace matador::backends::postgres { namespace matador::backends::postgres {
postgres_connection::string_to_int_map postgres_connection::statement_name_map_{}; postgres_connection::hash_to_string_map postgres_connection::statement_name_map_{};
postgres_connection::postgres_connection(const sql::connection_info &info) postgres_connection::postgres_connection(const sql::connection_info &info)
: connection_impl(info) { : connection_impl(info) {
} }
utils::result<void, utils::error> postgres_connection::open() { utils::result<void, utils::error> postgres_connection::open() {
@@ -34,7 +35,7 @@ utils::result<void, utils::error> postgres_connection::open() {
const std::string msg = PQerrorMessage(conn_); const std::string msg = PQerrorMessage(conn_);
PQfinish(conn_); PQfinish(conn_);
conn_ = nullptr; conn_ = nullptr;
return utils::failure(make_error(sql::error_code::OPEN_ERROR, nullptr, conn_, "Failed to connect")); return utils::failure(make_error(sql::error_code::OpenError, nullptr, conn_, "Failed to connect"));
} }
return utils::ok<void>(); return utils::ok<void>();
@@ -70,7 +71,7 @@ utils::result<utils::version, utils::error> postgres_connection::server_version(
const auto server_version = PQserverVersion(conn_); const auto server_version = PQserverVersion(conn_);
if (server_version == 0) { if (server_version == 0) {
return utils::failure(make_error(sql::error_code::FAILURE, nullptr, conn_, "Failed to get server version")); return utils::failure(make_error(sql::error_code::Failure, nullptr, conn_, "Failed to get server version"));
} }
return utils::ok(utils::version{ return utils::ok(utils::version{
@@ -82,137 +83,169 @@ utils::result<utils::version, utils::error> postgres_connection::server_version(
utils::basic_type oid2type(Oid oid); utils::basic_type oid2type(Oid oid);
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_connection::fetch(const sql::query_context &context) { utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_connection::fetch(const sql::query_context &ctx) {
PGresult *res = PQexec(conn_, context.sql.c_str()); PGresult *res = PQexec(conn_, ctx.sql.c_str());
if (is_result_error(res)) { if (is_result_error(res)) {
return utils::failure(make_error(sql::error_code::FETCH_FAILED, res, conn_, "Failed to fetch", context.sql)); const auto err = make_error(sql::error_code::FetchFailed, res, conn_, "Failed to fetch", ctx.sql);
PQclear(res);
return utils::failure(err);
} }
std::vector<object::attribute_definition> prototype = context.prototype; std::vector<object::attribute> prototype = ctx.prototype;
const int num_col = PQnfields(res); const int num_col = PQnfields(res);
if (prototype.size() != static_cast<size_t>(num_col)) { if (prototype.size() != static_cast<size_t>(num_col)) {
return utils::failure(make_error(sql::error_code::FETCH_FAILED, res, conn_, "Number of received columns doesn't match expected columns.", context.sql)); const auto err = make_error(sql::error_code::FetchFailed, res, conn_, "Number of received columns doesn't match expected columns.", ctx.sql);
PQclear(res);
return utils::failure(err);
} }
for (int i = 0; i < num_col; ++i) { for (int i = 0; i < num_col; ++i) {
if (!prototype.at(i).is_null()) { if (!prototype.at(i).is_null()) {
continue; continue;
} }
const auto type = oid2type(PQftype(res, i)); const auto type = oid2type(PQftype(res, i));
// const char *col_name = PQfname(res, i); // const char *col_name = PQfname(res, i);
// const auto size = PQfmod(res, i); // const auto size = PQfmod(res, i);
prototype.at(i).type(type); prototype.at(i).change_type(type);
} }
return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res), prototype)); return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res),
std::move(prototype),
ctx.resolver,
ctx.result_type));
} }
std::string postgres_connection::generate_statement_name(const sql::query_context &query) { std::string postgres_connection::generate_statement_name(const sql::query_context &ctx) {
std::stringstream name; auto it = statement_name_map_.find(ctx.sql_hash);
name << query.table.name << "_" << query.command_name;
auto result = postgres_connection::statement_name_map_.find(name.str());
if (result == postgres_connection::statement_name_map_.end()) { if (it == statement_name_map_.end()) {
result = postgres_connection::statement_name_map_.insert(std::make_pair(name.str(), 0)).first; it = statement_name_map_.insert(std::make_pair(ctx.sql_hash, utils::to_hex_string(ctx.sql_hash))).first;
} }
name << "_" << ++result->second; return it->second;
}
return name.str(); utils::result<sql::execute_result, utils::error> postgres_connection::execute(const std::string& stmt) const {
PGresult *res = PQexec(conn_, stmt.c_str());
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 size_t affected_rows = utils::to<size_t>(PQcmdTuples(res));
PQclear(res);
return utils::ok(sql::execute_result{affected_rows});
} }
utils::result<std::unique_ptr<sql::statement_impl>, utils::error> postgres_connection::prepare(const sql::query_context &context) { utils::result<std::unique_ptr<sql::statement_impl>, utils::error> postgres_connection::prepare(const sql::query_context &context) {
auto statement_name = postgres_connection::generate_statement_name(context); auto statement_name = generate_statement_name(context);
PGresult *result = PQprepare(conn_, statement_name.c_str(), context.sql.c_str(), PGresult *result = PQprepare(conn_, statement_name.c_str(), context.sql.c_str(), static_cast<int>(context.bind_vars.size()), nullptr);
static_cast<int>(context.bind_vars.size()), nullptr);
if (is_result_error(result)) { if (is_result_error(result)) {
return utils::failure(make_error(sql::error_code::PREPARE_FAILED, result, conn_, "Failed to prepare", context.sql)); return utils::failure(make_error(sql::error_code::PrepareFailed, result, conn_, "Failed to prepare", context.sql));
} }
std::unique_ptr<sql::statement_impl> s(std::make_unique<postgres_statement>(conn_, statement_name, context)); std::unique_ptr<sql::statement_impl> s(std::make_unique<postgres_statement>(conn_, result, statement_name, context));
return utils::ok(std::move(s)); return utils::ok(std::move(s));
} }
utils::result<size_t, utils::error> postgres_connection::execute(const std::string &stmt) { utils::result<sql::execute_result, utils::error> postgres_connection::execute(const sql::query_context &context) {
PGresult *res = PQexec(conn_, stmt.c_str()); return execute(context.sql);
PGresult *res = PQexec(conn_, context.sql.c_str());
if (const auto status = PQresultStatus(res); status != PGRES_COMMAND_OK && if (const auto status = PQresultStatus(res); status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
status != PGRES_TUPLES_OK) { return utils::failure(make_error(sql::error_code::Failure, res, conn_, "Failed to execute", context.sql));
return utils::failure(make_error(sql::error_code::FAILURE, res, conn_, "Failed to execute", stmt));
} }
const auto affected_rows = utils::to<size_t>(PQcmdTuples(res)); const size_t affected_rows = utils::to<size_t>(PQcmdTuples(res));
PQclear(res); PQclear(res);
return utils::ok(static_cast<size_t>(affected_rows)); return utils::ok(sql::execute_result{affected_rows});
} }
utils::basic_type oid2type(const Oid oid) { utils::basic_type oid2type(const Oid oid) {
switch (oid) { switch (oid) {
case 16: case 16:
return utils::basic_type::type_bool; return utils::basic_type::Boolean;
case 17: case 17:
return utils::basic_type::type_blob; return utils::basic_type::Blob;
case 18: case 18:
return utils::basic_type::type_int8; return utils::basic_type::Int8;
case 21: case 21:
return utils::basic_type::type_int16; return utils::basic_type::Int16;
case 23: case 23:
return utils::basic_type::type_int32; return utils::basic_type::Int32;
case 20: case 20:
return utils::basic_type::type_int64; return utils::basic_type::Int64;
case 25: case 25:
return utils::basic_type::type_text; return utils::basic_type::Text;
case 1043: case 1043:
return utils::basic_type::type_varchar; return utils::basic_type::Varchar;
case 700: case 700:
return utils::basic_type::type_float; return utils::basic_type::Float;
case 701: case 701:
return utils::basic_type::type_double; return utils::basic_type::Double;
case 1082: case 1082:
return utils::basic_type::type_date; return utils::basic_type::Date;
case 1114: case 1083:
return utils::basic_type::type_time; return utils::basic_type::Time;
default: case 1114:
return utils::basic_type::type_null; return utils::basic_type::DateTime;
} default:
return utils::basic_type::Null;
}
} }
utils::basic_type string2type(const char *type) { utils::basic_type string2type(const char *type) {
if (strcmp(type, "int2") == 0) { if (strcmp(type, "int2") == 0) {
return utils::basic_type::type_int16; return utils::basic_type::Int16;
} else if (strcmp(type, "int4") == 0) {
return utils::basic_type::type_int32;
} else if (strcmp(type, "int8") == 0) {
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 utils::basic_type::type_date;
} else if (strcmp(type, "timestamp") == 0) {
return utils::basic_type::type_time;
} else if (strcmp(type, "float4") == 0) {
return utils::basic_type::type_float;
} else if (strcmp(type, "float8") == 0) {
return utils::basic_type::type_double;
} else if (strncmp(type, "varchar", 7) == 0) {
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 utils::basic_type::type_null;
} }
if (strcmp(type, "int4") == 0) {
return utils::basic_type::Int32;
}
if (strcmp(type, "int8") == 0) {
return utils::basic_type::Int64;
}
if (strcmp(type, "bool") == 0) {
return utils::basic_type::Boolean;
}
if (strcmp(type, "date") == 0) {
return utils::basic_type::Date;
}
if (strcmp(type, "time") == 0) {
return utils::basic_type::Time;
}
if (strcmp(type, "timestamp") == 0) {
return utils::basic_type::DateTime;
}
if (strcmp(type, "float4") == 0) {
return utils::basic_type::Float;
}
if (strcmp(type, "float8") == 0) {
return utils::basic_type::Double;
}
if (strncmp(type, "varchar", 7) == 0) {
return utils::basic_type::Varchar;
}
if (strcmp(type, "character varying") == 0) {
return utils::basic_type::Varchar;
}
if (strcmp(type, "text") == 0) {
return utils::basic_type::Text;
}
if (strcmp(type, "bytea") == 0) {
return utils::basic_type::Blob;
}
return utils::basic_type::Null;
} }
utils::result<std::vector<object::attribute_definition>, utils::error> postgres_connection::describe(const std::string &table) { utils::result<std::vector<object::attribute>, utils::error> postgres_connection::describe(const std::string &table) {
const std::string stmt( 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='" "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 + "'"); + table + "'");
@@ -220,11 +253,11 @@ utils::result<std::vector<object::attribute_definition>, utils::error> postgres_
PGresult *res = PQexec(conn_, stmt.c_str()); PGresult *res = PQexec(conn_, stmt.c_str());
if (is_result_error(res)) { if (is_result_error(res)) {
return utils::failure(make_error(sql::error_code::DESCRIBE_FAILED, res, conn_, "Failed to describe", stmt)); return utils::failure(make_error(sql::error_code::DescribeFailed, res, conn_, "Failed to describe", stmt));
} }
postgres_result_reader reader(res); postgres_result_reader reader(res);
std::vector<object::attribute_definition> prototype; std::vector<object::attribute> prototype;
while (auto fetched = reader.fetch()) { while (auto fetched = reader.fetch()) {
if (!fetched.is_ok()) { if (!fetched.is_ok()) {
return utils::failure(fetched.release_error()); return utils::failure(fetched.release_error());
@@ -234,49 +267,66 @@ utils::result<std::vector<object::attribute_definition>, utils::error> postgres_
} }
char *end = nullptr; char *end = nullptr;
// Todo: Handle error // Todo: Handle error
auto index = strtoul(reader.column(0), &end, 10) - 1; // auto index = strtoul(reader.column(0), &end, 10) - 1;
std::string name = reader.column(1); std::string name = reader.column(1);
// Todo: extract size // Todo: extract size
auto type = (string2type(reader.column(2))); auto type = (string2type(reader.column(2)));
end = nullptr; end = nullptr;
object::null_option null_opt{object::null_option::NULLABLE}; object::null_option_type null_opt{object::null_option_type::Nullable};
if (strtoul(reader.column(4), &end, 10) == 0) { if (strtoul(reader.column(4), &end, 10) == 0) {
null_opt = object::null_option::NOT_NULL; null_opt = object::null_option_type::NotNull;
} }
// f.default_value(res->column(4)); // f.default_value(res->column(4));
prototype.emplace_back(name, type, utils::null_attributes, null_opt, index); prototype.emplace_back(name, type, NullAttributes, null_opt);
} }
return utils::ok(prototype); return utils::ok(prototype);
} }
utils::result<bool, utils::error> postgres_connection::exists(const std::string &schema_name, const std::string &table_name) { utils::result<bool, utils::error> postgres_connection::exists(const std::string &schema_name,
const std::string stmt( const std::string &table_name) {
"SELECT 1 FROM information_schema.tables WHERE table_schema = '" + schema_name + "' AND table_name = '" + table_name std::string stmt("SELECT 1 FROM information_schema.tables WHERE table_name = '" + table_name + "'");
+ "'"); if (!schema_name.empty()) {
stmt += " AND table_schema = '" + schema_name + "'";
}
PGresult *res = PQexec(conn_, stmt.c_str()); PGresult *res = PQexec(conn_, stmt.c_str());
if (is_result_error(res)) { 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 utils::failure(make_error(sql::error_code::TableExistsFailed, res, conn_, "Failed check if table exists", stmt));
} }
const auto result = utils::to<size_t>(PQcmdTuples(res)); const auto result = utils::to<size_t>(PQcmdTuples(res));
if (!result) { if (!result) {
return utils::failure(make_error(sql::error_code::FAILURE, res, conn_, "Failed to convert result value", stmt)); PQclear(res);
return utils::failure(make_error(sql::error_code::Failure, res, conn_, "Failed to convert result value", stmt));
} }
PQclear(res);
return utils::ok(*result == 1); return utils::ok(*result == 1);
} }
std::string postgres_connection::to_escaped_string(const utils::blob& value) const utils::result<bool, utils::error> postgres_connection::sequence_exists(const std::string& schema_name, const std::string& sequence_name) {
{ const std::string sql{"SELECT to_regclass('" + schema_name + "." + sequence_name + "')"};
PGresult* res = PQexec(conn_, sql.c_str());
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
return utils::failure(make_error(sql::error_code::SequenceExistsFailed, res, conn_, "Failed check if sequence exists", sql));
}
const bool exists = PQgetisnull(res, 0, 0) ? false : true;
PQclear(res);
return utils::ok(exists);
}
std::string postgres_connection::to_escaped_string(const utils::blob_type_t &value) const {
size_t escapedDataLength; size_t escapedDataLength;
unsigned char *escapedData = PQescapeByteaConn(conn_, value.data(), value.size(), &escapedDataLength); unsigned char *escapedData = PQescapeByteaConn(conn_, value.data(), value.size(), &escapedDataLength);
return {reinterpret_cast<char*>(escapedData), escapedDataLength-1}; return {reinterpret_cast<char *>(escapedData), escapedDataLength - 1};
} }
} }
extern "C" { extern "C" {
+11 -10
View File
@@ -4,8 +4,7 @@
#include "matador/utils/basic_types.hpp" #include "matador/utils/basic_types.hpp"
[[maybe_unused]] const matador::sql::dialect *get_dialect() [[maybe_unused]] const matador::sql::dialect *get_dialect() {
{
using namespace matador::sql; using namespace matador::sql;
const static dialect d = dialect_builder::builder() const static dialect d = dialect_builder::builder()
.create() .create()
@@ -13,15 +12,17 @@
return "$" + std::to_string(index); return "$" + std::to_string(index);
}) })
.with_token_replace_map({ .with_token_replace_map({
{dialect_token::BEGIN_BINARY_DATA, "'\\x"} {dialect_token::BeginBinaryData, "'\\x"},
}) {dialect_token::Identity, "GENERATED BY DEFAULT AS IDENTITY"}
})
.with_data_type_replace_map({ .with_data_type_replace_map({
{matador::utils::basic_type::type_int8, "SMALLINT"}, {matador::utils::basic_type::Int8, "SMALLINT"},
{matador::utils::basic_type::type_uint8, "SMALLINT"}, {matador::utils::basic_type::UInt8, "SMALLINT"},
{matador::utils::basic_type::type_float, "REAL"}, {matador::utils::basic_type::Float, "REAL"},
{matador::utils::basic_type::type_double, "DOUBLE PRECISION"}, {matador::utils::basic_type::Double, "DOUBLE PRECISION"},
{matador::utils::basic_type::type_time, "TIMESTAMP"}, {matador::utils::basic_type::Time, "TIME(6)"},
{matador::utils::basic_type::type_blob, "BYTEA"} {matador::utils::basic_type::DateTime, "TIMESTAMPTZ(6)"},
{matador::utils::basic_type::Blob, "BYTEA"}
}) })
.with_bool_strings("TRUE", "FALSE") .with_bool_strings("TRUE", "FALSE")
.with_default_schema_name("public") .with_default_schema_name("public")
+7 -7
View File
@@ -6,7 +6,10 @@
namespace matador::backends::postgres { namespace matador::backends::postgres {
utils::error make_error(const sql::error_code ec, const PGresult *res, const PGconn *db, const std::string &msg, utils::error make_error(const sql::error_code ec,
const PGresult *res,
const PGconn *db,
const std::string &msg,
const std::string &sql) { const std::string &sql) {
utils::error err(ec, msg); utils::error err(ec, msg);
err.add_error_info("dbms", "postgres"); err.add_error_info("dbms", "postgres");
@@ -30,22 +33,19 @@ bool is_result_error(const PGresult *res) {
return status != PGRES_TUPLES_OK && status != PGRES_COMMAND_OK; return status != PGRES_TUPLES_OK && status != PGRES_COMMAND_OK;
} }
void throw_postgres_error(const char *what, const std::string &source) void throw_postgres_error(const char *what, const std::string &source) {
{
std::stringstream msg; std::stringstream msg;
msg << "postgres error (" << source << "): " << what; msg << "postgres error (" << source << "): " << what;
throw std::logic_error(msg.str()); throw std::logic_error(msg.str());
} }
void throw_postgres_error(PGconn *db, const std::string &source) void throw_postgres_error(const PGconn *db, const std::string &source) {
{
if (PQstatus(db) == CONNECTION_BAD) { if (PQstatus(db) == CONNECTION_BAD) {
throw_postgres_error(PQerrorMessage(db), source); throw_postgres_error(PQerrorMessage(db), source);
} }
} }
void throw_postgres_error(PGresult *res, PGconn *db, const std::string &source, const std::string &sql) void throw_postgres_error(const PGresult *res, const PGconn *db, const std::string &source, const std::string &sql) {
{
if (res == nullptr) { if (res == nullptr) {
std::stringstream msg; std::stringstream msg;
msg << "postgres error (" << source << ", " << PQerrorMessage(db) << ": " << sql; msg << "postgres error (" << source << ", " << PQerrorMessage(db) << ": " << sql;
@@ -47,15 +47,15 @@ void bind_value(postgres_parameter_binder::bind_data &data, const size_t index,
postgres_parameter_binder::bind_data::bind_data(const size_t size) postgres_parameter_binder::bind_data::bind_data(const size_t size)
: strings(size) : strings(size)
, bytes(size) , bytes(size)
, values(size) , values(size)
, lengths(size) , lengths(size)
, formats(size) , formats(size) {
{} }
postgres_parameter_binder::postgres_parameter_binder(const size_t size) postgres_parameter_binder::postgres_parameter_binder(const size_t size)
: bind_data_(size) : bind_data_(size) {
{} }
void postgres_parameter_binder::write_value(const size_t pos, const int8_t &x) { void postgres_parameter_binder::write_value(const size_t pos, const int8_t &x) {
detail::bind_value(bind_data_, pos, x); detail::bind_value(bind_data_, pos, x);
@@ -123,31 +123,42 @@ void postgres_parameter_binder::write_value(const size_t pos, const std::string
write_value(pos, x); write_value(pos, x);
} }
void postgres_parameter_binder::write_value(const size_t pos, const time &/*x*/) { void postgres_parameter_binder::write_value(const size_t pos, const utils::date_type_t &x) {
// bind_data_.strings[pos] = utils::to_string(x, "%Y-%m-%d %T.%f"); char buf[11]; // "YYYY-MM-DD" + '\0'
bind_data_.values[pos] = bind_data_.strings[pos].data();
bind_data_.lengths[pos] = static_cast<int>(bind_data_.strings[pos].size()); std::snprintf(buf, sizeof(buf), "%04d-%02u-%02u", x.year, static_cast<unsigned>(x.month), static_cast<unsigned>(x.day));
bind_data_.formats[pos] = 0; bind_data_.strings[pos] = std::string{buf};
bind_data_.values[pos] = bind_data_.strings[pos].data();
bind_data_.lengths[pos] = static_cast<int>(bind_data_.strings[pos].size());
bind_data_.formats[pos] = 0;
} }
void postgres_parameter_binder::write_value(const size_t pos, const date &/*x*/) { void postgres_parameter_binder::write_value(const size_t pos, const utils::time_type_t &x) {
// bind_data_.strings[pos] = utils::to_string(x, utils::date_format::ISO8601); char buf[16]; // "HH:MM:SS.ffffff" + '\0'
bind_data_.values[pos] = bind_data_.strings[pos].data();
bind_data_.lengths[pos] = static_cast<int>(bind_data_.strings[pos].size()); std::snprintf(buf, sizeof(buf), "%02u:%02u:%02u.%06u",
bind_data_.formats[pos] = 0; static_cast<unsigned>(x.hour), static_cast<unsigned>(x.minute), static_cast<unsigned>(x.second), x.microsecond);
bind_data_.strings[pos] = std::string{buf};
bind_data_.values[pos] = bind_data_.strings[pos].data();
bind_data_.lengths[pos] = static_cast<int>(bind_data_.strings[pos].size());
bind_data_.formats[pos] = 0;
} }
void postgres_parameter_binder::write_value(const size_t pos, const utils::blob &x) { void postgres_parameter_binder::write_value(size_t pos, const utils::timestamp_type_t &x) {
}
void postgres_parameter_binder::write_value(const size_t pos, const utils::blob_type_t &x) {
bind_data_.bytes[pos] = x; bind_data_.bytes[pos] = x;
bind_data_.values[pos] = reinterpret_cast<char*>(bind_data_.bytes[pos].data()); bind_data_.values[pos] = reinterpret_cast<char *>(bind_data_.bytes[pos].data());
bind_data_.lengths[pos] = static_cast<int>(bind_data_.bytes[pos].size()); bind_data_.lengths[pos] = static_cast<int>(bind_data_.bytes[pos].size());
bind_data_.formats[pos] = 1; bind_data_.formats[pos] = 1;
} }
void postgres_parameter_binder::write_value(const size_t /*pos*/, const utils::value &/*x*/, size_t /*size*/) {} 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 { const postgres_parameter_binder::bind_data &postgres_parameter_binder::params() const {
return bind_data_; return bind_data_;
} }
} }
@@ -2,29 +2,26 @@
#include "matador/utils/convert.hpp" #include "matador/utils/convert.hpp"
#include "matador/utils/value.hpp" #include "matador/utils/value.hpp"
#include "matador/utils/string.hpp"
namespace matador::backends::postgres { namespace matador::backends::postgres {
postgres_result_reader::postgres_result_reader(PGresult *result) postgres_result_reader::postgres_result_reader(PGresult *result)
: result_(result) : result_(result)
, row_count_(PQntuples(result_)) , row_count_(PQntuples(result_))
, column_count_(PQnfields(result_)) , column_count_(PQnfields(result_)) {
{} }
postgres_result_reader::~postgres_result_reader() postgres_result_reader::~postgres_result_reader() {
{
if (result_) { if (result_) {
PQclear(result_); PQclear(result_);
} }
} }
size_t postgres_result_reader::column_count() const size_t postgres_result_reader::column_count() const {
{
return column_count_; return column_count_;
} }
const char *postgres_result_reader::column( const size_t index) const const char *postgres_result_reader::column(const size_t index) const {
{
return PQgetvalue(result_, static_cast<int>(row_index_), static_cast<int>(index)); return PQgetvalue(result_, static_cast<int>(row_index_), static_cast<int>(index));
} }
@@ -106,16 +103,28 @@ void postgres_result_reader::read_value(const char * /*id*/, const size_t index,
} }
} }
void postgres_result_reader::read_value(const char * /*id*/, const size_t /*index*/, time &/*value*/) { void postgres_result_reader::read_value(const char * /*id*/, const size_t index, utils::date_type_t &value) {
// if (const auto val = column(index); strlen(val) > 0) { if (const auto val = column(index); strlen(val) > 0) {
// value = time::parse(val, "%Y-%m-%d %T.%f"); if (const auto res = utils::to<utils::date_type_t>(std::string(val)); res.is_ok()) {
// } value = res.value();
}
}
} }
void postgres_result_reader::read_value(const char * /*id*/, const size_t /*index*/, date &/*value*/) { void postgres_result_reader::read_value(const char * /*id*/, const size_t index, utils::time_type_t &value) {
// if (const auto val = column(index); strlen(val) > 0) { if (const auto val = column(index); strlen(val) > 0) {
// value.set(val, matador::utils::date_format::ISO8601); if (const auto res = utils::to<utils::time_type_t>(std::string(val)); res.is_ok()) {
// } value = res.value();
}
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, utils::timestamp_type_t &value) {
if (const auto val = column(index); strlen(val) > 0) {
if (const auto res = utils::to<utils::timestamp_type_t>(std::string(val)); res.is_ok()) {
value = res.value();
}
}
} }
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, char *value, size_t size) { void postgres_result_reader::read_value(const char * /*id*/, const size_t index, char *value, size_t size) {
@@ -126,7 +135,7 @@ void postgres_result_reader::read_value(const char * /*id*/, const size_t index,
#else #else
strncpy(value, val, size); strncpy(value, val, size);
#endif #endif
value[size-1] = '\n'; value[size - 1] = '\n';
} else { } else {
#ifdef _MSC_VER #ifdef _MSC_VER
strcpy_s(value, size, val); strcpy_s(value, size, val);
@@ -144,69 +153,71 @@ void postgres_result_reader::read_value(const char * /*id*/, const size_t index,
value.assign(column(index)); value.assign(column(index));
} }
void postgres_result_reader::read_value( const char* /*id*/, const size_t index, utils::blob& value ) void postgres_result_reader::read_value(const char * /*id*/, const size_t index, utils::blob_type_t &value) {
{ const auto *data = reinterpret_cast<const unsigned char *>(column(index));
const auto *data = reinterpret_cast<const unsigned char*>(column(index));
// auto length = PQgetlength(result_, row_index_, static_cast<int>(index));
size_t length; size_t length;
unsigned char* unescaped = PQunescapeBytea(data, &length); unsigned char *unescaped = PQunescapeBytea(data, &length);
value.assign(unescaped, unescaped+length); value.assign(unescaped, unescaped + length);
PQfreemem(unescaped);
} }
template <typename Type> template<typename Type>
void set_value(const char* str, utils::value& value) { void set_value(const char *str, utils::value &value) {
if (const auto res = utils::to<Type>(str); res.is_ok()) { if (const auto res = utils::to<Type>(str); res.is_ok()) {
value = res.value(); value = res.value();
} }
} }
template <> template<>
void set_value<utils::blob>(const char* str, utils::value& value) { void set_value<utils::blob_type_t>(const char *str, utils::value &value) {
size_t length; size_t length;
unsigned char* unescaped = PQunescapeBytea(reinterpret_cast<const unsigned char*>(str), &length); unsigned char *unescaped = PQunescapeBytea(reinterpret_cast<const unsigned char *>(str), &length);
value = utils::blob(unescaped, unescaped+length); value = utils::blob_type_t(unescaped, unescaped + length);
PQfreemem(unescaped);
} }
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, utils::value &val, size_t) { void postgres_result_reader::read_value(const char * /*id*/, const size_t index, utils::value &val, size_t) {
switch (val.type()) { switch (val.type()) {
case utils::basic_type::type_int8: case utils::basic_type::Int8:
set_value<int8_t>(column(index), val); set_value<int8_t>(column(index), val);
break; break;
case utils::basic_type::type_int16: case utils::basic_type::Int16:
set_value<int16_t>(column(index), val); set_value<int16_t>(column(index), val);
break; break;
case utils::basic_type::type_int32: case utils::basic_type::Int32:
set_value<int32_t>(column(index), val); set_value<int32_t>(column(index), val);
break; break;
case utils::basic_type::type_int64: case utils::basic_type::Int64:
set_value<int64_t>(column(index), val); set_value<int64_t>(column(index), val);
break; break;
case utils::basic_type::type_uint8: case utils::basic_type::UInt8:
set_value<uint8_t>(column(index), val); set_value<uint8_t>(column(index), val);
break; break;
case utils::basic_type::type_uint16: case utils::basic_type::UInt16:
set_value<uint16_t>(column(index), val); set_value<uint16_t>(column(index), val);
break; break;
case utils::basic_type::type_uint32: case utils::basic_type::UInt32:
set_value<uint32_t>(column(index), val); set_value<uint32_t>(column(index), val);
break; break;
case utils::basic_type::type_uint64: case utils::basic_type::UInt64:
set_value<uint64_t>(column(index), val); set_value<uint64_t>(column(index), val);
break; break;
case utils::basic_type::type_float: case utils::basic_type::Float:
set_value<float>(column(index), val); set_value<float>(column(index), val);
break; break;
case utils::basic_type::type_double: case utils::basic_type::Double:
set_value<double>(column(index), val); set_value<double>(column(index), val);
break; break;
case utils::basic_type::type_bool: case utils::basic_type::Boolean:
set_value<bool>(column(index), val); set_value<bool>(column(index), val);
break; break;
case utils::basic_type::type_text: case utils::basic_type::Text:
case utils::basic_type::type_varchar: { case utils::basic_type::Varchar: {
if (const auto *column_value = column(index); column_value == nullptr) { if (const auto *column_value = column(index); column_value == nullptr) {
val = std::string{}; val = std::string{};
} else { } else {
@@ -214,24 +225,30 @@ void postgres_result_reader::read_value(const char * /*id*/, const size_t index,
} }
break; break;
} }
case utils::basic_type::type_time: case utils::basic_type::Time:
case utils::basic_type::type_date: { set_value<utils::time_type_t>(column(index), val);
val = std::string{column(index)}; break;
case utils::basic_type::Date:
set_value<utils::date_type_t>(column(index), val);
break;
case utils::basic_type::DateTime: {
set_value<utils::timestamp_type_t>(column(index), val);
break; break;
} }
case utils::basic_type::type_null: { case utils::basic_type::Null: {
val = nullptr_t{}; val = nullptr_t{};
break; break;
} }
case utils::basic_type::type_blob: { case utils::basic_type::Blob: {
set_value<utils::blob>(column(index), val); set_value<utils::blob_type_t>(column(index), val);
break; break;
} }
case utils::basic_type::Unknown:
break;
} }
} }
utils::attribute_reader &postgres_result_reader::result_binder() { utils::attribute_reader &postgres_result_reader::result_binder() {
return empty_binder_; return empty_binder_;
} }
} // namespace matador::backends::postgres } // namespace matador::backends::postgres
+48 -33
View File
@@ -1,20 +1,59 @@
#include "postgres_statement.hpp" #include "postgres_statement.hpp"
#include "matador/sql/execute_result.hpp"
#include "postgres_error.hpp" #include "postgres_error.hpp"
#include "postgres_parameter_binder.h" #include "postgres_parameter_binder.h"
#include "postgres_result_reader.hpp" #include "postgres_result_reader.hpp"
namespace matador::backends::postgres { namespace matador::backends::postgres {
postgres_statement::postgres_statement(PGconn *db, std::string name, const sql::query_context &query) postgres_statement::postgres_statement(PGconn *db, PGresult *res, std::string name, const sql::query_context &query)
: statement_impl(query) : statement_impl(query, 0)
, db_(db) , db_(db)
, res_(res)
, name_(std::move(name)) , name_(std::move(name))
{} {}
utils::result<size_t, utils::error> postgres_statement::execute(const sql::interface::parameter_binder& bindings) { postgres_statement::~postgres_statement() {
PQclear(res_);
}
utils::result<sql::execute_result, utils::error> postgres_statement::execute(const sql::parameter_binder& bindings) {
const auto* postgres_bindings = dynamic_cast<const postgres_parameter_binder*>(&bindings); const auto* postgres_bindings = dynamic_cast<const postgres_parameter_binder*>(&bindings);
if (!postgres_bindings) { if (!postgres_bindings) {
return utils::failure(utils::error(sql::error_code::EXECUTE_FAILED, "Failed to cast bindings to postgres bindings")); return utils::failure(utils::error(sql::error_code::ExecuteFailed, "Failed to cast bindings to postgres bindings"));
}
if (query_.command == sql::sql_command::Insert) {
// handle insert command with the primary key generator strategy
}
PGresult *res = PQexecPrepared(db_,
name_.c_str(),
static_cast<int>(postgres_bindings->params().values.size()),
postgres_bindings->params().values.data(),
postgres_bindings->params().lengths.data(),
postgres_bindings->params().formats.data(),
0);
if (is_result_error(res)) {
return utils::failure(make_error(sql::error_code::ExecuteFailed, res, db_, "Failed to execute statement", query_.sql));
}
size_t value{0};
if (const auto *tuples = PQcmdTuples(res); strlen(tuples) != 0) {
value = std::stoul(tuples);
}
PQclear(res);
return utils::ok(sql::execute_result{value});
}
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_statement::fetch(const sql::parameter_binder& bindings) {
const auto* postgres_bindings = dynamic_cast<const postgres_parameter_binder*>(&bindings);
if (!postgres_bindings) {
return utils::failure(utils::error(sql::error_code::ExecuteFailed, "Failed to cast bindings to postgres bindings"));
} }
PGresult *res = PQexecPrepared(db_, PGresult *res = PQexecPrepared(db_,
name_.c_str(), name_.c_str(),
@@ -25,39 +64,15 @@ utils::result<size_t, utils::error> postgres_statement::execute(const sql::inter
0); 0);
if (is_result_error(res)) { if (is_result_error(res)) {
return utils::failure(make_error(sql::error_code::EXECUTE_FAILED, res, db_, "Failed to execute statement", query_.sql)); return utils::failure(make_error(sql::error_code::FetchFailed, res, db_, "Failed to fetch statement", query_.sql));
} }
const auto *tuples = PQcmdTuples(res); return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res),
if (strlen(tuples) == 0) { query_.prototype,
return utils::ok(static_cast<size_t>(0)); query_.resolver,
} query_.result_type));
return utils::ok((std::stoul(tuples)));
} }
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_statement::fetch(const sql::interface::parameter_binder& bindings) {
const auto* postgres_bindings = dynamic_cast<const postgres_parameter_binder*>(&bindings);
if (!postgres_bindings) {
return utils::failure(utils::error(sql::error_code::EXECUTE_FAILED, "Failed to cast bindings to postgres bindings"));
}
PGresult *res = PQexecPrepared(db_,
name_.c_str(),
static_cast<int>(postgres_bindings->params().values.size()),
postgres_bindings->params().values.data(),
postgres_bindings->params().lengths.data(),
postgres_bindings->params().formats.data(),
0);
if (is_result_error(res)) {
return utils::failure(make_error(sql::error_code::FETCH_FAILED, res, db_, "Failed to fetch statement", query_.sql));
}
return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res), query_.prototype));
}
void postgres_statement::reset() {}
std::unique_ptr<utils::attribute_writer> postgres_statement::create_binder() const { std::unique_ptr<utils::attribute_writer> postgres_statement::create_binder() const {
return std::make_unique<postgres_parameter_binder>(query_.bind_vars.size()); return std::make_unique<postgres_parameter_binder>(query_.bind_vars.size());
} }
+26 -6
View File
@@ -1,18 +1,15 @@
CPMAddPackage("gh:catchorg/Catch2@3.7.1") CPMAddPackage("gh:catchorg/Catch2@3.14.0")
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
set(POSTGRES_CONNECTION_STRING "postgres://news:news@127.0.0.1:15432/matador") #set(POSTGRES_CONNECTION_STRING "postgres://test:test123!@127.0.0.1:15442/matador")
#set(POSTGRES_CONNECTION_STRING "postgres://test:test123!@127.0.0.1:15432/matador") set(POSTGRES_CONNECTION_STRING "postgres://test:test123!@127.0.0.1:5432/matador")
configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/postgres/test/connection.hpp @ONLY IMMEDIATE) configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/postgres/test/connection.hpp @ONLY IMMEDIATE)
message(STATUS "postgresql connection string: ${POSTGRES_CONNECTION_STRING}") message(STATUS "postgresql connection string: ${POSTGRES_CONNECTION_STRING}")
set(TEST_SOURCES set(TEST_SOURCES
../../../test/models/coordinate.hpp
../../../test/models/location.hpp
../../../test/models/types.hpp
../../../test/backends/ColorEnumTraits.cpp ../../../test/backends/ColorEnumTraits.cpp
../../../test/backends/ColorEnumTraits.hpp ../../../test/backends/ColorEnumTraits.hpp
../../../test/backends/ConnectionTest.cpp ../../../test/backends/ConnectionTest.cpp
@@ -22,12 +19,35 @@ set(TEST_SOURCES
../../../test/backends/QueryRecordTest.cpp ../../../test/backends/QueryRecordTest.cpp
../../../test/backends/QueryStatementTests.cpp ../../../test/backends/QueryStatementTests.cpp
../../../test/backends/QueryTest.cpp ../../../test/backends/QueryTest.cpp
../../../test/backends/SchemaFixture.cpp
../../../test/backends/SchemaFixture.hpp
../../../test/backends/SchemaTest.cpp
../../../test/backends/SequenceFixture.cpp
../../../test/backends/SequenceFixture.hpp
../../../test/backends/SequenceTest.cpp
../../../test/backends/SessionDeleteHasMany.cpp
../../../test/backends/SessionDeleteHasManyToMany.cpp
../../../test/backends/SessionFixture.cpp ../../../test/backends/SessionFixture.cpp
../../../test/backends/SessionFixture.hpp ../../../test/backends/SessionFixture.hpp
../../../test/backends/SessionInsertBelongsTo.cpp
../../../test/backends/SessionInsertHasMany.cpp
../../../test/backends/SessionInsertHasManyToManyTest.cpp
../../../test/backends/SessionInsertHasOne.cpp
../../../test/backends/SessionTest.cpp ../../../test/backends/SessionTest.cpp
../../../test/backends/StatementCacheTest.cpp ../../../test/backends/StatementCacheTest.cpp
../../../test/backends/StatementTest.cpp ../../../test/backends/StatementTest.cpp
../../../test/backends/TableSequenceFixture.cpp
../../../test/backends/TableSequenceFixture.hpp
../../../test/backends/TableSequenceTest.cpp
../../../test/backends/TypeTraitsTest.cpp ../../../test/backends/TypeTraitsTest.cpp
../../../test/models/author.hpp
../../../test/models/coordinate.hpp
../../../test/models/location.hpp
../../../test/models/model_metas.hpp
../../../test/models/types.hpp
../../../test/models/user.hpp
../../../test/utils/RecordPrinter.cpp
../../../test/utils/RecordPrinter.hpp
) )
set(LIBRARY_TEST_TARGET PostgresTests) set(LIBRARY_TEST_TARGET PostgresTests)
+9 -1
View File
@@ -1,4 +1,6 @@
add_executable(demo main.cpp) add_executable(demo main.cpp
../include/matador/query/query_column.hpp)
target_link_libraries(demo PRIVATE target_link_libraries(demo PRIVATE
matador-core matador-core
matador-orm matador-orm
@@ -23,3 +25,9 @@ target_link_libraries(work PRIVATE
) )
add_dependencies(work matador-postgres) add_dependencies(work matador-postgres)
add_executable(object object.cpp)
target_link_libraries(object PRIVATE
matador-core
${CMAKE_DL_LIBS}
)
+6 -5
View File
@@ -20,14 +20,15 @@ struct author {
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process( Operator& op ) {
namespace field = matador::access; using namespace matador;
namespace field = access;
field::primary_key( op, "id", id ); field::primary_key( op, "id", id );
field::attribute( op, "first_name", first_name, 63 ); field::attribute( op, "first_name", first_name, VarChar63 );
field::attribute( op, "last_name", last_name, 63 ); field::attribute( op, "last_name", last_name, VarChar63 );
field::attribute( op, "date_of_birth", date_of_birth, 31 ); field::attribute( op, "date_of_birth", date_of_birth, VarChar63 );
field::attribute( op, "year_of_birth", year_of_birth ); field::attribute( op, "year_of_birth", year_of_birth );
field::attribute( op, "distinguished", distinguished ); field::attribute( op, "distinguished", distinguished );
field::has_many( op, "books", books, "author_id", matador::utils::default_foreign_attributes ); field::has_many( op, "books", books, "author_id", utils::CascadeNoneFetchLazy );
} }
}; };
} }
+4 -3
View File
@@ -15,10 +15,11 @@ struct book {
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process( Operator& op ) {
namespace field = matador::access; using namespace matador;
namespace field = access;
field::primary_key( op, "id", id ); field::primary_key( op, "id", id );
field::attribute( op, "title", title, 511 ); field::attribute( op, "title", title, VarChar511 );
field::belongs_to( op, "author_id", book_author, matador::utils::default_foreign_attributes ); field::belongs_to( op, "author_id", book_author, utils::CascadeNoneFetchLazy );
field::attribute( op, "published_in", published_in ); field::attribute( op, "published_in", published_in );
} }
}; };
+250 -227
View File
@@ -1,285 +1,308 @@
#include "matador/sql/column.hpp"
#include "matador/sql/connection.hpp" #include "matador/sql/connection.hpp"
#include "matador/sql/query_macro.hpp" #include "matador/sql/connection_pool.hpp"
#include "matador/query/condition.hpp" #include "matador/query/criteria.hpp"
#include "matador/query/query.hpp"
#include "matador/query/table_column.hpp"
#include "matador/query/meta_table_macro.hpp"
#include "matador/query/schema.hpp"
#include "matador/object/object_ptr.hpp" #include "matador/object/object_ptr.hpp"
#include "matador/object/schema.hpp" #include "matador/object/repository.hpp"
#include "matador/utils/access.hpp" #include "matador/utils/access.hpp"
#include "matador/utils/default_type_traits.hpp" #include "matador/utils/default_type_traits.hpp"
#include "matador/utils/enum_mapper.hpp" #include "matador/utils/enum_mapper.hpp"
#include "matador/utils/types.hpp"
#include "matador/sql/query_macro.hpp"
#include <iostream> #include <iostream>
#include <string>
struct author { struct author {
unsigned int id{}; unsigned int id{};
std::string first_name; std::string first_name;
std::string last_name; std::string last_name;
std::string date_of_birth; std::string date_of_birth;
unsigned short year_of_birth{}; unsigned short year_of_birth{};
bool distinguished{false}; bool distinguished{false};
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process(Operator &op) {
namespace field = matador::access; namespace field = matador::access;
field::primary_key( op, "id", id ); field::primary_key(op, "id", id);
field::attribute( op, "first_name", first_name, 63 ); field::attribute(op, "first_name", first_name, 63);
field::attribute( op, "last_name", last_name, 63 ); field::attribute(op, "last_name", last_name, 63);
field::attribute( op, "date_of_birth", date_of_birth, 31 ); field::attribute(op, "date_of_birth", date_of_birth, 31);
field::attribute( op, "year_of_birth", year_of_birth ); field::attribute(op, "year_of_birth", year_of_birth);
field::attribute( op, "distinguished", distinguished ); field::attribute(op, "distinguished", distinguished);
} }
}; };
struct book { struct book {
unsigned int id{}; unsigned int id{};
matador::object::object_ptr<author> book_author; matador::object::object_ptr<author> book_author;
std::string title; std::string title;
unsigned short published_in{}; unsigned short published_in{};
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process(Operator &op) {
namespace field = matador::access; namespace field = matador::access;
field::primary_key( op, "id", id ); field::primary_key(op, "id", id);
field::attribute( op, "title", title, 511 ); field::attribute(op, "title", title, 511);
field::has_one( op, "author_id", book_author, matador::utils::default_foreign_attributes ); field::belongs_to(op, "author_id", book_author, matador::utils::CascadeNoneFetchLazy);
field::attribute( op, "published_in", published_in ); field::attribute(op, "published_in", published_in);
} }
}; };
struct payload { struct payload {
unsigned int id{}; unsigned int id{};
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process(Operator &op) {
namespace field = matador::access; namespace field = matador::access;
field::primary_key( op, "id", id ); field::primary_key(op, "id", id);
} }
}; };
struct job { struct job {
enum class job_state { enum class job_state {
Pending, Pending,
Running, Running,
Succeeded, Succeeded,
Failed, Failed,
Canceled Canceled
}; };
enum class job_mode { enum class job_mode {
Foreground, Foreground,
Background Background
}; };
unsigned int id{}; unsigned int id{};
matador::object::object_ptr<payload> data; matador::object::object_ptr<payload> data;
std::string type; std::string type;
std::string description; std::string description;
job_state state; job_state state;
job_mode mode; job_mode mode;
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process(Operator &op) {
namespace field = matador::access; namespace field = matador::access;
field::primary_key( op, "id", id ); field::primary_key(op, "id", id);
field::belongs_to( op, "payload", data, matador::utils::default_foreign_attributes ); field::belongs_to(op, "payload", data, matador::utils::CascadeNoneFetchLazy);
field::attribute( op, "type", type, 511 ); field::attribute(op, "type", type, 511);
field::attribute( op, "description", description, 511 ); field::attribute(op, "description", description, 511);
field::attribute( op, "state", state ); field::attribute(op, "state", state);
field::attribute( op, "mode", mode ); field::attribute(op, "mode", mode);
} }
}; };
static const matador::utils::enum_mapper<job::job_state> job_state_enum({ static const matador::utils::enum_mapper<job::job_state> job_state_enum({
{job::job_state::Pending, "Pending"}, {job::job_state::Pending, "Pending"},
{job::job_state::Running, "Running"}, {job::job_state::Running, "Running"},
{job::job_state::Succeeded, "Succeeded"}, {job::job_state::Succeeded, "Succeeded"},
{job::job_state::Failed, "Failed"}, {job::job_state::Failed, "Failed"},
{job::job_state::Canceled, "Canceled"} {job::job_state::Canceled, "Canceled"}
}); });
static const matador::utils::enum_mapper<job::job_mode> job_mode_enum({ static const matador::utils::enum_mapper<job::job_mode> job_mode_enum({
{job::job_mode::Foreground, "Foreground"}, {job::job_mode::Foreground, "Foreground"},
{job::job_mode::Background, "Background"} {job::job_mode::Background, "Background"}
}); });
template<> template<>
struct matador::utils::data_type_traits<job::job_state, void> { struct matador::utils::data_type_traits<job::job_state, void> {
static basic_type type(const std::size_t size) { return data_type_traits<std::string>::type(size); } static basic_type type(const std::size_t size) { return data_type_traits<std::string>::type(size); }
static void read_value(attribute_reader &reader, const char *id, const size_t index, job::job_state &value) { static void read_value(attribute_reader &reader, const char *id, const size_t index, job::job_state &value) {
std::string enum_string; std::string enum_string;
reader.read_value(id, index, enum_string, 64); reader.read_value(id, index, enum_string, 64);
if (const auto enum_opt = job_state_enum.to_enum(enum_string)) { if (const auto enum_opt = job_state_enum.to_enum(enum_string)) {
value = enum_opt.value(); value = enum_opt.value();
}
} }
}
static void write_value(attribute_writer &binder, const size_t index, const job::job_state &value) { static void write_value(attribute_writer &binder, const size_t index, const job::job_state &value) {
binder.write_value(index, job_state_enum.to_string(value)); binder.write_value(index, job_state_enum.to_string(value));
} }
}; };
template<> template<>
struct matador::utils::data_type_traits<job::job_mode, void> { struct matador::utils::data_type_traits<job::job_mode, void> {
static basic_type type( const std::size_t size) { return data_type_traits<std::string>::type(size); } static basic_type type(const std::size_t size) { return data_type_traits<std::string>::type(size); }
static void read_value(attribute_reader &reader, const char *id, const size_t index, job::job_mode &value) { static void read_value(attribute_reader &reader, const char *id, const size_t index, job::job_mode &value) {
std::string enum_string; std::string enum_string;
reader.read_value(id, index, enum_string, 64); reader.read_value(id, index, enum_string, 64);
if (const auto enum_opt = job_mode_enum.to_enum(enum_string)) { if (const auto enum_opt = job_mode_enum.to_enum(enum_string)) {
value = enum_opt.value(); value = enum_opt.value();
}
} }
}
static void bind_value(attribute_writer &binder, const size_t index, const job::job_mode &value) { static void bind_value(attribute_writer &binder, const size_t index, const job::job_mode &value) {
binder.write_value(index, job_mode_enum.to_string(value)); binder.write_value(index, job_mode_enum.to_string(value));
} }
}; };
QUERY_HELPER( authors, id, first_name, last_name, date_of_birth, year_of_birth, distinguished ) META_TABLE(authors, AUTHOR, id, first_name, last_name, date_of_birth, year_of_birth, distinguished)
QUERY_HELPER( books, id, author_id, title, published_in ) META_TABLE(books, BOOK, id, author_id, title, published_in)
QUERY_HELPER( job, id, payload, type, description, state, mode ) META_TABLE(job, JOB, id, payload, type, description, state, mode)
QUERY_HELPER( payload, id ) META_TABLE(payload, PAYLOAD, id)
QUERY_HELPER( temporary_table, id ); META_TABLE(temporary_table, TEMPORARY_TABLE, id);
META_TABLE(customer, CUSTOMER, id, name, email, address)
META_TABLE(product, PRODUCT, id, title, description, price, category)
META_TABLE(category, CATEGORY, id, title, description)
META_TABLE(cart, CART, id, items, owner)
int main() { int main() {
using namespace matador::sql; using namespace matador::sql;
using namespace matador::object; using namespace matador::object;
using namespace matador::utils; using namespace matador::utils;
using namespace matador::query;
using namespace matador::query::meta;
const std::string env_var{"MATADOR_BACKENDS_PATH"}; const std::string env_var{"MATADOR_BACKENDS_PATH"};
std::string dns{"sqlite://demo.db"}; connection_pool pool("postgres://test:test123!@127.0.0.1:5432/matador", 4);
schema s( "main" );
auto result = s.attach<author>( "authors" ).and_then( [&s] {
return s.attach<book>( "books" );
} );
// s.attach<book>( "books" );
connection c( dns ); schema s("");
result = c.open(); auto result = s.attach<author>("authors").and_then([&s] {
// s.create( c ); return s.attach<book>("books");
// });
// auto create_authors_sql = c.query( s )
// .create()
// .table<author>( qh::authors )
// .execute();
//
// c.query( s )
// .create()
// .table<book>( qh::books )
// .execute();
//
// std::cout << "SQL: " << create_authors_sql << "\n";
//
// author mc;
// mc.id = 1;
// mc.first_name = "Michael";
// mc.last_name = "Crichton";
// mc.date_of_birth = "19.8.1954";
// mc.year_of_birth = 1954;
// mc.distinguished = true;
// auto insert_authors_sql = c.query( s )
// .insert()
// .into( qh::authors )
// .values( mc )
// .execute();
//
// std::cout << "SQL: " << insert_authors_sql << "\n";
//
// auto result = c.query( s )
// .select( qh::authors.columns )
// .from( qh::authors )
// .fetch_all();
//
// for (const auto& row: result) { std::cout << "Author " << row.at( qh::authors.first_name ) << "\n"; }
//
// auto update_authors_sql = c.query( s )
// .update( qh::authors )
// .set( {{qh::authors.first_name, "Stephen"},
// {qh::authors.last_name, "King"}} )
// .where( qh::authors.last_name == "Crichton" )
// .execute();
//
// std::cout << "SQL: " << update_authors_sql << "\n";
//
// auto authors = c.query( s )
// .select( qh::authors.columns )
// .from( qh::authors )
// .fetch_all<author>();
//
// for (const auto& a: authors) { std::cout << "Author " << a.first_name << "\n"; }
//
// c.query( s )
// .insert()
// .into( qh::books )
// .values( {2, "It", mc.id, 1980} )
// .execute();
//
// c.query( s )
// .insert()
// .into( qh::books )
// .values( {3, "Misery", mc.id, 1984} )
// .execute();
//
// auto select_books_sql = c.query( s )
// .select( qh::books.columns, {qh::authors.last_name} )
// .from( qh::books )
// .join_left( qh::authors )
// .on( qh::books.author_id == qh::authors.id )
// .where( qh::books.published_in < 2008 && qh::authors.last_name == "King" )
// .group_by( qh::books.published_in )
// .order_by( qh::books.title ).asc()
// .limit( 5 )
// .offset( 2 )
// .fetch_all();
//
// for (const auto& r: select_books_sql) { std::cout << "R: " << r.at( qh::books.title ) << ", " << r.at( qh::authors.last_name ) << "\n"; }
// // SELECT book.title, book.id, book.author_id, book.published_in, author.name
// // FROM book
// // INNER JOIN author ON book.author_id = author.id
// // WHERE book.published_in < 2008 AND author.name = "Michael Crichton"
// // ORDER BY "book.title" ASC
// // OFFSET 2 LIMIT 5
//
// c.query( s ).drop().table( qh::books ).execute();
//
// auto drop_authors_sql = c.query( s )
// .drop()
// .table( qh::authors )
// .execute();
//
// std::cout << "SQL: " << drop_authors_sql << "\n";
//
// auto res = c.query( s )
// .select( {qh::payload.id} )
// .from( qh::payload )
// .join_left( qh::job )
// .on( qh::job.payload == qh::payload.id )
// .where(
// in( qh::payload.id, c.query( s )
// .select( {qh::job.state} )
// .from( qh::job )
// .where( qh::job.state == job::job_state::Running )
// ) &&
// in( qh::payload.id, c.query( s )
// .select( {qh::temporary_table.id} )
// .from( qh::temporary_table ) )
// )
// .build();
// // .fetch_value<unsigned long>();
// std::cout << "SQL: " << res.sql << "\n";
const auto c = pool.acquire();
result = s.create(*c);
if (!result) {
std::cout << "error: " << result.err() << std::endl;
return 0; return 0;
} }
author mc;
mc.id = 1;
mc.first_name = "Michael";
mc.last_name = "Crichton";
mc.date_of_birth = "19.8.1954";
mc.year_of_birth = 1954;
mc.distinguished = true;
auto insert_authors_sql = insert()
.into(AUTHOR)
.values(mc)
.execute(*c);
if (!insert_authors_sql) {
std::cout << "error: " << insert_authors_sql.err() << std::endl;
std::ignore = s.drop(*c);
return 0;
}
auto authors_result = select(AUTHOR)
.from(AUTHOR)
.fetch_all(*c);
if (!authors_result) {
std::cout << "error: " << authors_result.err() << std::endl;
std::ignore = s.drop(*c);
return 0;
}
for (const auto &row: *authors_result) {
std::cout << "Author " << row.at(AUTHOR.first_name.canonical_name()) << "\n";
}
//
// auto update_authors_sql = c.query( s )
// .update( qh::authors )
// .set( {{qh::authors.first_name, "Stephen"},
// {qh::authors.last_name, "King"}} )
// .where( qh::authors.last_name == "Crichton" )
// .execute();
//
// std::cout << "SQL: " << update_authors_sql << "\n";
//
// auto authors = c.query( s )
// .select( qh::authors.columns )
// .from( qh::authors )
// .fetch_all<author>();
//
// for (const auto& a: authors) { std::cout << "Author " << a.first_name << "\n"; }
//
// c.query( s )
// .insert()
// .into( qh::books )
// .values( {2, "It", mc.id, 1980} )
// .execute();
//
// c.query( s )
// .insert()
// .into( qh::books )
// .values( {3, "Misery", mc.id, 1984} )
// .execute();
//
// auto select_books_sql = select( BOOK, {AUTHOR.last_name} )
auto select_books_sql = select({BOOK.id, BOOK.title, BOOK.author_id, BOOK.published_in, AUTHOR.last_name})
.from(BOOK)
.join_left(AUTHOR)
.on(BOOK.author_id == AUTHOR.id)
.where(BOOK.published_in < 2008 && AUTHOR.last_name == "King")
.group_by({BOOK.id, AUTHOR.last_name})
.order_by(BOOK.title).asc()
.limit(5)
.offset(2)
.fetch_all(*c);
if (!select_books_sql) {
std::cout << "error: " << select_books_sql.err() << std::endl;
std::ignore = s.drop(*c);
return 0;
}
for (const auto &r: *select_books_sql) {
std::cout << "R: " << r.at(BOOK.title.canonical_name()) << ", " << r.at(AUTHOR.last_name.canonical_name()) << "\n";
}
result = s.drop(*c);
if (!result) {
std::cout << "error: " << result.err() << std::endl;
return 0;
}
// // SELECT book.title, book.id, book.author_id, book.published_in, author.name
// // FROM book
// // INNER JOIN author ON book.author_id = author.id
// // WHERE book.published_in < 2008 AND author.name = "Michael Crichton"
// // ORDER BY "book.title" ASC
// // OFFSET 2 LIMIT 5
//
// c.query( s ).drop().table( qh::books ).execute();
//
// auto drop_authors_sql = c.query( s )
// .drop()
// .table( qh::authors )
// .execute();
//
// std::cout << "SQL: " << drop_authors_sql << "\n";
//
auto res = select({PAYLOAD.id})
.from(PAYLOAD)
.join_left(JOB)
.on(JOB.payload == PAYLOAD.id)
.where(
in(PAYLOAD.id, select({JOB.state})
.from(JOB)
.where(JOB.state == job::job_state::Running)
) &&
in(PAYLOAD.id, select({TEMPORARY_TABLE.id})
.from(TEMPORARY_TABLE)
)
).compile(c->dialect());
// // .fetch_value<unsigned long>();
std::cout << "SQL: " << res.sql << "\n";
return 0;
}
+233
View File
@@ -0,0 +1,233 @@
#include "matador/utils/basic_types.hpp"
#include "matador/utils/constraints.hpp"
#include "matador/utils/field_attributes.hpp"
#include <string>
#include <vector>
namespace object {
namespace utils = matador::utils;
class object;
enum class null_option_type : uint8_t {
Nullable,
NotNull
};
class attribute {
public:
explicit attribute(std::string name); // NOLINT(*-explicit-constructor)
attribute(const attribute&) = default;
attribute& operator=(const attribute&) = default;
attribute(attribute&&) noexcept = default;
attribute& operator=(attribute&&) noexcept = default;
attribute() = default;
attribute(std::string name,
utils::basic_type type,
const utils::field_attributes& opts = matador::NullAttributes,
null_option_type null_opt = null_option_type::NotNull)
: name_(std::move(name)), type_(type), options_(opts), null_option_type_(null_opt) {}
[[nodiscard]] const std::string& name() const { return name_; }
void name(const std::string& n) { name_ = n; }
[[nodiscard]] const utils::field_attributes& options() const { return options_; }
[[nodiscard]] utils::field_attributes& options();
[[nodiscard]] bool is_nullable() const { return null_option_type_ == null_option_type::Nullable; }
[[nodiscard]] utils::basic_type type() const { return type_; }
[[nodiscard]] bool is_integer() const { return type_ >= utils::basic_type::Int8 && type_ <= utils::basic_type::UInt64; }
[[nodiscard]] bool is_floating_point() const { return type_ == utils::basic_type::Float || type_ == utils::basic_type::Double; }
[[nodiscard]] bool is_bool() const { return type_ == utils::basic_type::Boolean; }
[[nodiscard]] bool is_string() const { return type_ == utils::basic_type::Text; }
[[nodiscard]] bool is_varchar() const { return type_ == utils::basic_type::Varchar; }
[[nodiscard]] bool is_date() const { return type_ == utils::basic_type::Date; }
[[nodiscard]] bool is_time() const { return type_ == utils::basic_type::Time; }
[[nodiscard]] bool is_blob() const { return type_ == utils::basic_type::Blob; }
[[nodiscard]] bool is_null() const { return type_ == utils::basic_type::Null; }
private:
friend class object;
std::string name_;
std::string alias_;
utils::basic_type type_{utils::basic_type::Null};
utils::field_attributes options_;
null_option_type null_option_type_{null_option_type::NotNull};
object* parent_{nullptr};
};
class constraint {
public:
constraint() = default;
explicit constraint(std::string name) : name_(std::move(name)) {}
[[nodiscard]] const std::string& name() const { return name_; }
private:
friend class object;
std::string name_;
std::string attribute_name_;
utils::constraints options_{utils::constraints::None};
object* parent_{nullptr};
attribute* attribute_{nullptr};
};
class object {
public:
explicit object(std::string name, std::string alias = "");
void add_attribute(attribute attr) {
auto &ref = attributes_.emplace_back(std::move(attr));
ref.parent_ = this;
}
static const attribute& create_attribute(std::string name, object& obj) {
attribute attr{std::move(name)};
attr.parent_ = &obj;
return obj.attributes_.emplace_back(std::move(attr));
}
void add_constraint(constraint c) {
auto &ref = constraints_.emplace_back(std::move(c));
ref.parent_ = this;
}
[[nodiscard]] const std::string& name() const { return name_; }
[[nodiscard]] const std::string& alias() const { return alias_; }
[[nodiscard]] bool has_attributes() const { return attributes_.empty(); }
[[nodiscard]] size_t attribute_count() const { return attributes_.size(); }
[[nodiscard]] const std::vector<attribute>& attributes() const { return attributes_; }
[[nodiscard]] bool has_constraints() const { return constraints_.empty(); }
[[nodiscard]] size_t constraint_count() const { return constraints_.size(); }
[[nodiscard]] const std::vector<constraint>& constraints() const { return constraints_; }
private:
std::string name_;
std::string alias_;
std::vector<attribute> attributes_;
std::vector<constraint> constraints_;
};
template<typename Type>
class typed_object : public object {
public:
using object::object;
Type as(std::string alias) { return Type{std::move(alias)}; }
};
struct column_builder {
explicit column_builder(std::string column_name)
: column_name( std::move(column_name) ) {
}
column_builder& not_null() {
return *this;
}
column_builder& primary_key() {
return *this;
}
operator attribute() const {
return attribute{column_name};
}
std::string column_name;
};
column_builder column(std::string name) {
return column_builder(std::move(name));
}
namespace matador::sql {
struct constraint {
std::string name;
};
}
struct constraint_builder {
constraint_builder& constraint(std::string name) {
constraint_name = std::move(name);
return *this;
}
constraint_builder& primary_key(std::string name) {
pk_column_name = std::move(name);
return *this;
}
constraint_builder& foreign_key(std::string name) {
fk_column_name = std::move(name);
return *this;
}
constraint_builder& references(std::string table, std::string column) {
this->table_name = std::move(table);
this->column_name = std::move(column);
return *this;
}
operator matador::sql::constraint() const {
return matador::sql::constraint{constraint_name};
}
std::string constraint_name;
std::string pk_column_name;
std::string fk_column_name;
std::string table_name;
std::string column_name;
};
constraint_builder constraint(std::string name) {
constraint_builder builder;
return builder.constraint(std::move(name));
}
template<typename Type>
std::string column_prefix(typed_object<Type> *tab) {
if (!tab || tab->empty()) {
return "";
}
if (!tab->alias().empty()) {
return tab->alias();
}
return tab->name();
}
struct table_builder {
explicit table_builder(std::string name)
: table_name( std::move(name) ) {}
table_builder& as(std::string table_alias) {
this->alias = std::move(table_alias);
return *this;
}
operator object() const {
return object{table_name, alias};
}
std::string table_name;
std::string alias;
};
table_builder table(std::string name) {
return table_builder(std::move(name));
}
}
int main() {
}
+10 -10
View File
@@ -13,8 +13,7 @@
namespace demo { namespace demo {
struct recipe; struct recipe;
struct ingredient struct ingredient {
{
unsigned int id{}; unsigned int id{};
std::string name; std::string name;
matador::object::collection<matador::object::object_ptr<demo::recipe>> recipes{}; matador::object::collection<matador::object::object_ptr<demo::recipe>> recipes{};
@@ -25,15 +24,15 @@ struct ingredient
template<class Operator> template<class Operator>
void process(Operator &op) { void process(Operator &op) {
namespace field = matador::access; using namespace matador;
namespace field = access;
field::primary_key(op, "id", id); field::primary_key(op, "id", id);
field::attribute(op, "name", name, 255); field::attribute(op, "name", name, VarChar255);
field::has_many_to_many(op, "recipe_ingredients", recipes, "ingredient_id", "recipe_id", matador::utils::fetch_type::EAGER); field::has_many_to_many(op, "recipe_ingredients", recipes, "ingredient_id", "recipe_id", utils::CascadeAllFetchEager);
} }
}; };
struct recipe struct recipe {
{
unsigned int id{}; unsigned int id{};
std::string name; std::string name;
matador::object::collection<matador::object::object_ptr<demo::ingredient>> ingredients{}; matador::object::collection<matador::object::object_ptr<demo::ingredient>> ingredients{};
@@ -44,10 +43,11 @@ struct recipe
template<class Operator> template<class Operator>
void process(Operator &op) { void process(Operator &op) {
namespace field = matador::access; using namespace matador;
namespace field = access;
field::primary_key(op, "id", id); field::primary_key(op, "id", id);
field::attribute(op, "name", name, 255); field::attribute(op, "name", name, VarChar255);
field::has_many_to_many(op, "recipe_ingredients", ingredients, matador::utils::fetch_type::LAZY); field::has_many_to_many(op, "recipe_ingredients", ingredients, utils::CascadeAllFetchLazy);
} }
}; };
+58 -46
View File
@@ -1,4 +1,7 @@
#include "matador/object/schema.hpp" #include "matador/object/repository.hpp"
#include "matador/object/collection.hpp"
#include "matador/sql/resolver_service.hpp"
#include "matador/logger/log_manager.hpp" #include "matador/logger/log_manager.hpp"
@@ -65,7 +68,7 @@ struct names {
void process(Operator &op) { void process(Operator &op) {
namespace field = matador::access; namespace field = matador::access;
field::primary_key( op, "id", id ); field::primary_key( op, "id", id );
field::has_many(op, "name_list", names_list, "names_id", matador::utils::fetch_type::EAGER); field::has_many(op, "name_list", names_list, "names_id", matador::utils::fetch_type::Eager);
} }
}; };
@@ -78,11 +81,12 @@ struct profile {
template<typename Operator> template<typename Operator>
void process(Operator &op) { void process(Operator &op) {
namespace field = matador::access; using namespace matador;
namespace field = access;
field::primary_key( op, "id", id ); field::primary_key( op, "id", id );
field::attribute( op, "first_name", first_name, 255 ); field::attribute( op, "first_name", first_name, VarChar255 );
field::attribute( op, "last_name", last_name, 255 ); field::attribute( op, "last_name", last_name, VarChar255 );
field::belongs_to( op, "user_id", user, matador::utils::default_foreign_attributes ); field::belongs_to( op, "user_id", user, utils::CascadeNoneFetchLazy );
} }
}; };
struct user { struct user {
@@ -92,10 +96,11 @@ struct user {
template<typename Operator> template<typename Operator>
void process(Operator &op) { void process(Operator &op) {
namespace field = matador::access; using namespace matador;
namespace field = access;
field::primary_key( op, "id", id ); field::primary_key( op, "id", id );
field::attribute( op, "username", username, 255 ); field::attribute( op, "username", username, VarChar255 );
field::has_one(op, "profile_id", profile, matador::utils::default_foreign_attributes ); field::has_one(op, "profiles", profile, "profile_id", utils::CascadeNoneFetchLazy );
} }
}; };
@@ -105,9 +110,10 @@ struct person {
template<typename Operator> template<typename Operator>
void process(Operator &op) { void process(Operator &op) {
using namespace matador;
namespace field = matador::access; namespace field = matador::access;
field::primary_key( op, "id", id ); field::primary_key( op, "id", id );
field::attribute( op, "name", name, 255 ); field::attribute( op, "name", name, VarChar255 );
} }
}; };
@@ -119,7 +125,7 @@ struct person_repo {
void process(Operator &op) { void process(Operator &op) {
namespace field = matador::access; namespace field = matador::access;
field::primary_key( op, "id", id ); field::primary_key( op, "id", id );
field::has_many( op, "person_list", person_list, "person_id", matador::utils::default_foreign_attributes ); field::has_many( op, "person_list", person_list, "person_id", matador::utils::CascadeNoneFetchLazy );
} }
}; };
} }
@@ -131,84 +137,90 @@ int main() {
// logger::default_min_log_level(logger::log_level::LVL_DEBUG); // logger::default_min_log_level(logger::log_level::LVL_DEBUG);
// logger::add_log_sink(logger::create_stdout_sink()); // logger::add_log_sink(logger::create_stdout_sink());
sql::resolver_service rs;
std::weak_ptr cr = rs.joined_collection_resolver<object::object_ptr<names>>( typeid(person), "names" );
utils::identifier id{1};
object::collection_proxy cp(cr, id);
{ {
// has_many with builtin-type // has_many with builtin-type
object::schema schema; object::repository repo;
auto result = schema.attach<names>("names"); auto result = repo.attach<names>("names");
schema.dump(std::cout); repo.dump(std::cout);
} }
{ {
// has_many with foreign without belongs_to // has_many with foreign without belongs_to
object::schema schema; object::repository repo;
auto result = schema.attach<person_repo>("person_repo") auto result = repo.attach<person_repo>("person_repo")
.and_then([&schema] { return schema.attach<person>("persons"); }); .and_then([&repo] { return repo.attach<person>("persons"); });
schema.dump(std::cout); repo.dump(std::cout);
} }
{ {
// has_many with foreign without belongs_to // has_many with foreign without belongs_to
object::schema schema; object::repository repo;
auto result = schema.attach<person>("persons") auto result = repo.attach<person>("persons")
.and_then([&schema] { return schema.attach<person_repo>("person_repo"); }); .and_then([&repo] { return repo.attach<person_repo>("person_repo"); });
schema.dump(std::cout); repo.dump(std::cout);
} }
{ {
// has_many to belongs_to // has_many to belongs_to
object::schema schema; object::repository repo;
auto result = schema.attach<author>("authors") auto result = repo.attach<author>("authors")
.and_then([&schema] { return schema.attach<book>("books"); }); .and_then([&repo] { return repo.attach<book>("books"); });
schema.dump(std::cout); repo.dump(std::cout);
} }
{ {
// belongs_to to has_many // belongs_to to has_many
object::schema schema; object::repository repo;
auto result = schema.attach<book>("books") auto result = repo.attach<book>("books")
.and_then([&schema] { return schema.attach<author>("authors"); }); .and_then([&repo] { return repo.attach<author>("authors"); });
schema.dump(std::cout); repo.dump(std::cout);
} }
{ {
// has_many_to_many (with join columns first) // has_many_to_many (with join columns first)
object::schema schema; object::repository repo;
auto result = schema.attach<demo::ingredient>("ingredients") auto result = repo.attach<demo::ingredient>("ingredients")
.and_then([&schema] { return schema.attach<recipe>("recipes"); }); .and_then([&repo] { return repo.attach<recipe>("recipes"); });
schema.dump(std::cout); repo.dump(std::cout);
} }
{ {
// has_many_to_many (with join columns last) // has_many_to_many (with join columns last)
object::schema schema; object::repository repo;
auto result = schema.attach<demo::recipe>("recipes") auto result = repo.attach<demo::recipe>("recipes")
.and_then([&schema] { return schema.attach<ingredient>("ingredients"); }); .and_then([&repo] { return repo.attach<ingredient>("ingredients"); });
schema.dump(std::cout); repo.dump(std::cout);
} }
{ {
// belongs_to to has_one // belongs_to to has_one
object::schema schema; object::repository repo;
auto result = schema.attach<profile>("profiles") auto result = repo.attach<profile>("profiles")
.and_then([&schema] { return schema.attach<user>("users"); }); .and_then([&repo] { return repo.attach<user>("users"); });
schema.dump(std::cout); repo.dump(std::cout);
} }
{ {
// has_one to belongs_to // has_one to belongs_to
object::schema schema; object::repository repo;
auto result = schema.attach<user>("users") auto result = repo.attach<user>("users")
.and_then([&schema] { return schema.attach<profile>("profiles"); }); .and_then([&repo] { return repo.attach<profile>("profiles"); });
schema.dump(std::cout); repo.dump(std::cout);
} }
} }
+304 -54
View File
@@ -17,28 +17,20 @@
#include "work/jobs/IdListPayload.hpp" #include "work/jobs/IdListPayload.hpp"
#include "matador/utils/default_type_traits.hpp" #include "matador/utils/default_type_traits.hpp"
#include "matador/object/schema.hpp" #include "matador/object/repository.hpp"
#include "matador/sql/connection.hpp" #include "matador/sql/connection.hpp"
#include "matador/orm/session.hpp" #include "matador/query/schema.hpp"
template <> struct matador::utils::data_type_traits<work::core::timestamp, void> { #include "matador/query/session.hpp"
static basic_type type(std::size_t /*size*/) { return basic_type::type_uint64; }
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 matador::utils::data_type_traits<work::core::UserInfo, void> { template <> struct matador::utils::data_type_traits<work::core::UserInfo, void> {
static basic_type type(std::size_t /*size*/) { return basic_type::type_uint64; } static basic_type type(std::size_t /*size*/) { return basic_type::UInt64; }
static void read_value(attribute_reader &/*reader*/, const char * /*id*/, size_t /*index*/, nullptr_t &/*value*/) { static void read_value(attribute_reader &/*reader*/, const char * /*id*/, size_t /*index*/, work::core::UserInfo &/*value*/) {
} }
static void bind_value(attribute_writer &/*binder*/, size_t /*index*/, nullptr_t &/*value*/) { static void bind_value(attribute_writer &/*binder*/, size_t /*index*/, work::core::UserInfo &/*value*/) {
} }
}; };
@@ -56,9 +48,9 @@ using namespace work::models;
// //
// If all checks succeed, the requested is fetched from // If all checks succeed, the requested is fetched from
// the database. // the database.
// schema.attach<jobs::Payload>("payloads", make_polymorph("type")); // schema.attach_as_base<jobs::Payload>("payloads", make_polymorph("type"));
// schema.attach<jobs::Payload, jobs::IdPayload>("id_list_payloads", make_polymorph_type("IdPayload")); // schema.attach<jobs::Payload, jobs::IdPayload>("id_list_payloads", make_polymorph_type("IdPayload"));
// schema.attach<jobs::Payload, jobs::IdListPayload>("id_payloads"make_polymorph_type("IdListPayload")); // schema.attach<jobs::Payload, jobs::IdListPayload>("id_payloads", make_polymorph_type("IdListPayload"));
// object::object_ptr<jobs::Payload> payload; // object::object_ptr<jobs::Payload> payload;
// auto result = payload.as<jobs::IdPayload>(); // auto result = payload.as<jobs::IdPayload>();
// if (result.is_ok()) { // if (result.is_ok()) {
@@ -70,55 +62,313 @@ using namespace work::models;
// payload.is_polymorphic_type<jobs::IdPayload>(); // payload.is_polymorphic_type<jobs::IdPayload>();
int main() { int main() {
// logger::default_min_log_level(logger::log_level::LVL_DEBUG); logger::default_min_log_level(logger::log_level::Debug);
// logger::add_log_sink(logger::create_stdout_sink()); logger::add_log_sink(logger::create_stdout_sink());
const object::schema schema("Administration");
// sql::connection_pool<sql::connection> pool("postgres://news:news@127.0.0.1:15432/matador", 4); // sql::connection_pool<sql::connection> pool("postgres://news:news@127.0.0.1:15432/matador", 4);
sql::connection_pool pool("postgres://test:test123!@127.0.0.1:5432/matador", 4); sql::connection_pool pool("postgres://test:test123!@127.0.0.1:15442/matador", 4);
orm::session ses(pool); utils::message_bus bus;
// query::schema ses({bus, pool});
query::schema admin_schema("Administration");
auto result = ses.attach<admin::CollectionCenter>("collection_centers") auto result = admin_schema.attach<admin::CollectionCenter>("collection_centers")
.and_then([&ses] { return ses.attach<admin::UserDirectory>("user_directories"); }) .and_then([&admin_schema] { return admin_schema.attach<admin::UserDirectory>("user_directories"); })
.and_then([&ses] { return ses.attach<admin::LdapGroupSchemaSettings>("ldap_group_schema_settings"); }) .and_then([&admin_schema] { return admin_schema.attach<admin::LdapGroupSchemaSettings>("ldap_group_schema_settings"); })
.and_then([&ses] { return ses.attach<admin::LdapImportSettings>("ldap_import_settings"); }) .and_then([&admin_schema] { return admin_schema.attach<admin::LdapImportSettings>("ldap_import_settings"); })
.and_then([&ses] { return ses.attach<admin::LdapUserSchemaSettings>("ldap_user_schema_settings"); }) .and_then([&admin_schema] { return admin_schema.attach<admin::LdapUserSchemaSettings>("ldap_user_schema_settings"); })
.and_then([&ses] { return ses.attach<admin::InternalUserDirectory, admin::UserDirectory>("internal_user_directories"); }) .and_then([&admin_schema] { return admin_schema.attach<admin::InternalUserDirectory, admin::UserDirectory>("internal_user_directories"); })
.and_then([&ses] { return ses.attach<admin::LdapUserDirectory, admin::UserDirectory>("ldap_user_directories"); } ) .and_then([&admin_schema] { return admin_schema.attach<admin::LdapUserDirectory, admin::UserDirectory>("ldap_user_directories"); } )
.and_then([&ses] { return ses.attach<admin::LoginHistory>("login_histories"); }) .and_then([&admin_schema] { return admin_schema.attach<admin::LoginHistory>("login_histories"); })
.and_then([&ses] { return ses.attach<admin::Scenario>("scenarios"); }) .and_then([&admin_schema] { return admin_schema.attach<admin::Scenario>("scenarios"); })
.and_then([&ses] { return ses.attach<admin::User>("users"); }) .and_then([&admin_schema] { return admin_schema.attach<admin::User>("users"); })
.and_then([&ses] { return ses.attach<admin::UserSession>("user_sessions"); }) .and_then([&admin_schema] { return admin_schema.attach<admin::UserSession>("user_sessions"); })
.and_then([&ses] { return ses.attach<jobs::Job>("jobs"); }) .and_then([&admin_schema] { return admin_schema.attach<jobs::Job>("jobs"); })
.and_then([&ses] { return ses.attach<jobs::Payload>("payloads"); }) .and_then([&admin_schema] { return admin_schema.attach<jobs::Payload>("payloads"); })
.and_then([&ses] { return ses.attach<jobs::IdPayload, jobs::Payload>("id_list_payloads"); }) .and_then([&admin_schema] { return admin_schema.attach<jobs::IdPayload, jobs::Payload>("id_list_payloads"); })
.and_then([&ses] { return ses.attach<jobs::IdListPayload, jobs::Payload>("id_payloads"); }) .and_then([&admin_schema] { return admin_schema.attach<jobs::IdListPayload, jobs::Payload>("id_payloads"); })
.and_then([&ses] { return ses.attach<jobs::Task>("tasks"); }) .and_then([&admin_schema] { return admin_schema.attach<jobs::Task>("tasks"); })
.and_then([&admin_schema, &pool] { return admin_schema.create( *pool.acquire() ); })
; ;
ses.dump_schema(std::cout); admin_schema.dump(std::cout);
if (!result) { if (!result) {
std::cout << "error: " << result.err().message() << std::endl; std::cout << "error: " << result.err().message() << std::endl;
return 0; return 0;
} }
result = ses.create_schema(); const auto conn = pool.acquire();
if (!result) { // result = admin_schema.create(*conn);
std::cout << "error: " << result.err() << std::endl; // if (!result) {
return 0; // std::cout << "error: " << result.err() << std::endl;
} // return 0;
// }
// const std::string dns{"sqlite://demo.db"}; // result = admin_schema.drop(*conn);
// sql::connection c(dns); // if (!result) {
// // std::cout << "error: " << result.err() << std::endl;
// result = c.open();
//
// // orm::session s()
// if (!result.is_ok()) {
// return 0; // return 0;
// } // }
return 0; /*
} const auto statement = QString( "SELECT %5.%6, %1.%2 FROM %3 %1, %4 %5 WHERE %1.%2=%5.%6 AND %7" )
.arg( payloadTableAliasName,
payloadSqlMetaInfo.columnName( Payload::Attributes::idAttributeId() ),
Payload::staticSqlMetaInfo().tableName(),
Task::staticSqlMetaInfo().tableName(),
taskTableAliasName,
taskSqlMetaInfo.columnName( Task::Attributes::payloadAttributeId() ),
filter->idSelectStatement( taskTableAliasName ) );
*/
using namespace matador::query::meta;
using namespace matador::utils;
const auto payloads = PAYLOAD.as( "payloads" );
const auto tasks = TASK.as( "tasks" );
const auto stmt1 = query::select( { tasks.payload, payloads.id } )
.from( tasks, payloads )
.where ( payloads.id == tasks.payload )
.str( conn->dialect() );
/*
const auto temporaryTable = status.addChildExc( fillTemporaryTable( idsInsidePayload ) );
const auto lookUpTableName = temporaryTable->tableName();
const auto& jobSqlMetaInfo = Job::staticSqlMetaInfo();
const auto& payloadSqlMetaInfo = Payload::staticSqlMetaInfo();
const auto& idPayloadSqlMetaInfo = IdPayload::staticSqlMetaInfo();
const auto statement = QString( "SELECT iPT.%1 FROM %2 iPT INNER JOIN %3 pT ON iPT.%4 = pT.%5 WHERE pT.%5 IN (SELECT %6 FROM %7 WHERE %8=?) AND iPT.%1 IN (SELECT %9 FROM %10)" )
.arg( idPayloadSqlMetaInfo.columnName( IdPayload::Attributes::payloadIdAttributeId() ) ) // %1
.arg( idPayloadSqlMetaInfo.tableName() ) // %2
.arg( payloadSqlMetaInfo.tableName() ) // %3
.arg( idPayloadSqlMetaInfo.columnName( IdPayload::Attributes::idAttributeId() ) ) // %4
.arg( payloadSqlMetaInfo.columnName( Payload::Attributes::idAttributeId() ) ) // %5
.arg( jobSqlMetaInfo.columnName( Job::Attributes::payloadAttributeId() ) ) // %6
.arg( jobSqlMetaInfo.tableName() ) // %7
.arg( jobSqlMetaInfo.columnName( Job::Attributes::stateSqlAttributeId() ) ) // %8
.arg( lookUpColName() ) // %9
.arg( lookUpTableName ); // %10
*/
query::table_column temp_id_col("ID");
query::table temporary("TempTable", { temp_id_col } );
const auto iPT = ID_PAYLOAD.as("iPT");
const auto pT = PAYLOAD.as("pT");
const auto stmt2 = query::select({ iPT.payload_id })
.from( iPT )
.join_left( pT ).on( iPT.id == pT.id )
.where(
in( pT.id, query::select({ JOB.payload }).from( JOB ).where( JOB.state == _) ) &&
in( iPT.payload_id, query::select({ temp_id_col }).from( temporary ) )
)
.str( conn->dialect() );
// const QString statement = QString( "SELECT %5 FROM %1 "
// "LEFT JOIN (SELECT %2, MAX(%3) AS lastSuccessfulLogin FROM %1 WHERE %4 = ? GROUP BY %2) ll "
// "ON %1.%2 = ll.%2 "
// "WHERE (lastSuccessfulLogin IS NULL OR %3 <= lastSuccessfulLogin) AND %3 < ?" )
// .arg( LoginHistory::staticSqlMetaInfo().tableName() )
// .arg( LoginHistory::staticSqlMetaInfo().columnName( LoginHistory::Attributes::userAttributeId() ) )
// .arg( LoginHistory::staticSqlMetaInfo().columnName( LoginHistory::Attributes::timestampAttributeId() ) )
// .arg( LoginHistory::staticSqlMetaInfo().columnName( LoginHistory::Attributes::failReasonSqlAttributeId() ) )
// .arg( LoginHistory::staticSqlMetaInfo().columnName( LoginHistory::Attributes::idAttributeId() ) );
const auto q = query::select({LOGIN_HISTORY.client, query::maximum(LOGIN_HISTORY.login_time).as( "lastSuccessfulLogin" ) } )
.from( LOGIN_HISTORY )
.where( LOGIN_HISTORY.fail_reason == _ )
.group_by( LOGIN_HISTORY.client );
// .as_table("ll");
const auto stmt3 = query::select({ LOGIN_HISTORY.client })
.from( LOGIN_HISTORY )
.join_left(q/*, "ll"*/ )
.on( LOGIN_HISTORY.client == "ll.client" )
.where( query::is_null("lastSuccessfulLogin") && LOGIN_HISTORY.login_time < "ll.lastSuccessfulLogin" )
.str( conn->dialect() );
// const QString subQuery = QString( "SELECT MAX(%1) AS lastSuccessfulLogin, %3 FROM %2 "
// "WHERE(%4 = ? OR %4 = ? OR %4 = ?) AND %3 = ? GROUP BY %3 " );
//
// const QString statement = QString( "SELECT MAX(t1.%1) AS LAST_FAILED_LOGIN FROM %2 t1 LEFT JOIN"
// "(%0) t2 "
// "ON t1.%3 = t2.%3 "
// "WHERE(t2.lastSuccessfulLogin IS NULL OR t2.lastSuccessfulLogin < t1.%1) "
// "AND (t1.%4 = ? OR t1.%4 = ?) "
// "AND t1.%3 = ?" )
// .arg( subQuery )
// .arg( LoginHistory::staticSqlMetaInfo().columnName( LoginHistory::Attributes::timestampAttributeId() ) )
// .arg( LoginHistory::staticSqlMetaInfo().tableName() )
// .arg( columnFilterName )
// .arg( LoginHistory::staticSqlMetaInfo().columnName( LoginHistory::Attributes::failReasonSqlAttributeId() ) );
//
// QSqlQuery query( sqlDatabase() );
// if ( query.prepare( statement ) ) {
// query.addBindValue( LoginHistory::FailReason::NoFailReason );
// query.addBindValue( LoginHistory::FailReason::NoScenarioForUser );
// query.addBindValue( LoginHistory::FailReason::UserLockedByAdmin );
// query.addBindValue( columnFilterValue );
// query.addBindValue( LoginHistory::FailReason::WrongPassword );
// query.addBindValue( LoginHistory::FailReason::UnknownUserName );
// query.addBindValue( columnFilterValue );
// const auto temporaryTable = status.addChildExc( fillTemporaryTable( idsInsidePayload ) );
//
// const auto& jobSqlMetaInfo = Job::staticSqlMetaInfo();
// const auto& payloadSqlMetaInfo = Payload::staticSqlMetaInfo();
// const auto& idPayloadSqlMetaInfo = IdPayload::staticSqlMetaInfo();
//
// auto filter = std::make_unique<SqlTools::GenericFilter>( new SqlTools::AttributeDefaultFilter( jobSqlMetaInfo,
// Job::Attributes::idAttributeId(),
// Job::Attributes::idAttributeId(),
// Job::Attributes::typeAttributeId(),
// SqlTools::AttributeDefaultFilter::Operation::Equal,
// jobName ),
// new SqlTools::InFilter( payloadSqlMetaInfo.tableName(),
// jobSqlMetaInfo.columnName( Job::Attributes::payloadAttributeId() ),
// payloadSqlMetaInfo.columnName( Payload::Attributes::idAttributeId() ),
// new SqlTools::InFilter( idPayloadSqlMetaInfo.tableName(),
// payloadSqlMetaInfo.columnName( Payload::Attributes::idAttributeId() ),
// idPayloadSqlMetaInfo.columnName( IdPayload::Attributes::idAttributeId() ),
// new SqlTools::TemporaryInFilter( temporaryTable,
// idPayloadSqlMetaInfo.columnName( IdPayload::Attributes::payloadIdAttributeId() ),
// lookUpColName() ) ) ),
// SqlTools::GenericFilter::Operation::And );
//
// if ( !jobStates.empty() ) {
// auto it = jobStates.begin();
// std::unique_ptr<SqlTools::GenericFilter> criteria = std::make_unique<SqlTools::AttributeDefaultFilter>( jobSqlMetaInfo,
// Job::Attributes::idAttributeId(),
// Job::Attributes::idAttributeId(),
// Job::Attributes::stateSqlAttributeId(),
// SqlTools::AttributeDefaultFilter::Operation::Equal,
// *it++ );
// for ( ; it != jobStates.end(); ++it ) {
// criteria = std::make_unique<SqlTools::GenericFilter>( new SqlTools::AttributeDefaultFilter( jobSqlMetaInfo,
// Job::Attributes::idAttributeId(),
// Job::Attributes::idAttributeId(),
// Job::Attributes::stateSqlAttributeId(),
// SqlTools::AttributeDefaultFilter::Operation::Equal,
// *it ),
// criteria.release(),
// SqlTools::GenericFilter::Operation::Or );
// }
//
// filter = std::make_unique<SqlTools::GenericFilter>( criteria.release(), filter.release(), SqlTools::GenericFilter::Operation::And );
// }
//
// const auto ids = status.addChildExc( this->ids( SqlManager::jobFilter, filter.get() ) );
// SELECT
// ID
// FROM
// NEWS_JOBS
// WHERE
// NEWS_JOBS.ID > ?
// AND (NEWS_JOBS.TYPE = ?
// AND ( NEWS_JOBS.PAYLOAD IN (
// SELECT
// ID
// FROM
// NEWS_PAYLOADS
// WHERE
// NEWS_PAYLOADS.ID IN (
// SELECT
// ID
// FROM
// NEWS_ID_PAYLOADS
// WHERE
// NEWS_ID_PAYLOADS.PAYLOAD_ID IN (SELECT ID FROM NEWS_TMP_3499274F12B644179DBD25BB60C48529)
// )
// )
// )
// )
const auto stmt4 = query::select({JOB.id})
.from(JOB)
.where( JOB.id > _ && JOB.name == _ && in( JOB.payload, query::select({PAYLOAD.id}).from(PAYLOAD).where(
in( PAYLOAD.id, query::select({ID_PAYLOAD.id}).from(ID_PAYLOAD).where(
in( ID_PAYLOAD.id, query::select({temp_id_col}).from(temporary))
) ) ) ) )
.str( conn->dialect() );
// SELECT DISTINCT nj.ID
// FROM NEWS_JOBS nj
// JOIN NEWS_PAYLOADS np
// ON nj.PAYLOAD = np.ID
// JOIN NEWS_ID_PAYLOADS nip
// ON np.ID = nip.ID
// JOIN NEWS_TMP_3499274F12B644179DBD25BB60C48529 tmp
// ON nip.PAYLOAD_ID = tmp.ID
// WHERE nj.ID > ?
// AND nj.TYPE = ?;
const auto stmt5 = query::select({JOB.id})
.from(JOB)
.join_left(PAYLOAD).on(JOB.payload == PAYLOAD.id)
.join_left(ID_PAYLOAD).on(PAYLOAD.id == ID_PAYLOAD.id)
.join_left(temporary).on(ID_PAYLOAD.id == temp_id_col)
.where(JOB.id > _ && JOB.name == _)
.str(conn->dialect());
std::cout << stmt1 << std::endl;
std::cout << stmt2 << std::endl;
std::cout << stmt3 << std::endl;
std::cout << stmt4 << std::endl;
std::cout << stmt5 << std::endl;
return 0;
}
/*
SELECT
"tasks"."payload", "payloads"."id"
FROM
"public"."tasks", "public"."payloads"
WHERE
"payloads"."id" = "tasks"."payload"
SELECT
"iPT"."payload_id" FROM "public"."id_payloads" "iPT"
LEFT JOIN
"public"."payloads" "pT"
ON
"iPT"."id" = "pT"."id"
WHERE ("pT"."id" IN (
SELECT
"jobs"."payload"
FROM
"public"."jobs"
WHERE
"jobs"."state" = $1
) AND "iPT"."payload_id" IN (
SELECT
"ID"
FROM "public"."TempTable"
)
)
SELECT
"login_histories"."client"
FROM
"public"."login_histories"
LEFT JOIN (
SELECT
"login_histories"."client", MAX(login_time) AS lastSuccessfulLogin
FROM
"public"."login_histories"
WHERE
"login_histories"."fail_reason" = $1
GROUP BY
"login_histories"."client"
)
ON
"login_histories"."client" = 'll.client'
WHERE
("lastSuccessfulLogin" IS NULL AND "login_histories"."login_time" < 'll.lastSuccessfulLogin')
*/
+8 -3
View File
@@ -11,6 +11,8 @@
#include "matador/utils/fetch_type.hpp" #include "matador/utils/fetch_type.hpp"
#include "matador/utils/types.hpp" #include "matador/utils/types.hpp"
#include "matador/query/meta_table_macro.hpp"
namespace work::models::admin { namespace work::models::admin {
struct User; struct User;
@@ -28,21 +30,24 @@ static const matador::utils::enum_mapper<CollectionCenterType> CollectionCenterT
struct CollectionCenter : core::Model { struct CollectionCenter : core::Model {
std::string name; std::string name;
matador::utils::blob symbol; matador::utils::blob_type_t symbol;
CollectionCenterType type{CollectionCenterType::NoType}; CollectionCenterType type{CollectionCenterType::NoType};
matador::object::collection<matador::object::object_ptr<User>> users; matador::object::collection<matador::object::object_ptr<User>> users;
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process( Operator& op ) {
using namespace matador;
namespace field = matador::access; namespace field = matador::access;
field::process( op, *matador::base_class<Model>( this ) ); field::process( op, *matador::base_class<Model>( this ) );
field::attribute( op, "name", name, 511 ); field::attribute( op, "name", name, UniqueVarChar511 );
field::attribute( op, "symbol", symbol ); field::attribute( op, "symbol", symbol );
field::attribute( op, "type", type ); field::attribute( op, "type", type );
field::has_many( op, "collection_center_users", users, "users_id", matador::utils::fetch_type::LAZY ); field::has_many( op, "collection_center_users", users, "users_id", utils::CascadeAllFetchLazy );
} }
}; };
} }
META_TABLE(collection_center, COLLECTION_CENTER, id, version, name, symbol, type)
#endif //COLLECTION_CENTER_HPP #endif //COLLECTION_CENTER_HPP
+1 -1
View File
@@ -15,4 +15,4 @@ struct InternalUserDirectory : UserDirectory {
}; };
} }
#endif //INTERNALUSERDIRECTORY_HPP #endif //INTERNAL_USER_DIRECTORY_HPP
+9 -7
View File
@@ -4,22 +4,24 @@
#include "../core/Model.hpp" #include "../core/Model.hpp"
#include "matador/utils/base_class.hpp" #include "matador/utils/base_class.hpp"
#include "matador/utils/foreign_attributes.hpp"
namespace work::models::admin { namespace work::models::admin {
struct LdapUserDirectory; struct LdapUserDirectory;
struct LdapGroupSchemaSettings : core::Model { struct LdapGroupSchemaSettings : core::Model {
std::string group_object_filter; std::string group_object_filter{};
std::string user_member_attribute; std::string user_member_attribute{};
matador::object::object_ptr<LdapUserDirectory> ldap_user_directory; matador::object::object_ptr<LdapUserDirectory> ldap_user_directory{};
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process( Operator& op ) {
namespace field = matador::access; using namespace matador;
namespace field = access;
field::process( op, *matador::base_class<Model>( this ) ); field::process( op, *matador::base_class<Model>( this ) );
field::has_one(op, "ldap_user_directory", ldap_user_directory, matador::utils::default_foreign_attributes); field::has_one(op, "ldap_user_directory", ldap_user_directory, utils::CascadeAllFetchLazy);
field::attribute( op, "group_object_filter", group_object_filter, 511 ); field::attribute( op, "group_object_filter", group_object_filter, VarChar511 );
field::attribute( op, "user_member_attribute", user_member_attribute, 511 ); field::attribute( op, "user_member_attribute", user_member_attribute, VarChar511 );
} }
}; };
+5 -3
View File
@@ -4,6 +4,7 @@
#include "../core/Model.hpp" #include "../core/Model.hpp"
#include "matador/utils/base_class.hpp" #include "matador/utils/base_class.hpp"
#include "matador/utils/foreign_attributes.hpp"
namespace work::models::admin { namespace work::models::admin {
struct LdapUserDirectory; struct LdapUserDirectory;
@@ -16,10 +17,11 @@ struct LdapImportSettings : core::Model {
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process( Operator& op ) {
namespace field = matador::access; using namespace matador;
namespace field = access;
field::process( op, *matador::base_class<Model>( this ) ); field::process( op, *matador::base_class<Model>( this ) );
field::has_one(op, "ldap_user_directory", ldap_user_directory, matador::utils::default_foreign_attributes); field::has_one(op, "ldap_user_directory", ldap_user_directory, utils::CascadeAllFetchLazy);
field::attribute( op, "default_role", default_role, 511 ); field::attribute( op, "default_role", default_role, VarChar511 );
field::attribute( op, "sync_interval", sync_interval ); field::attribute( op, "sync_interval", sync_interval );
field::attribute( op, "network_timeout", network_timeout ); field::attribute( op, "network_timeout", network_timeout );
} }
+11 -9
View File
@@ -10,6 +10,7 @@
#include "matador/object/object_ptr.hpp" #include "matador/object/object_ptr.hpp"
#include "matador/utils/base_class.hpp" #include "matador/utils/base_class.hpp"
#include "matador/utils/foreign_attributes.hpp"
namespace work::models::admin { namespace work::models::admin {
struct LdapUserDirectory : UserDirectory { struct LdapUserDirectory : UserDirectory {
@@ -24,16 +25,17 @@ struct LdapUserDirectory : UserDirectory {
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process( Operator& op ) {
namespace field = matador::access; using namespace matador;
namespace field = access;
field::process( op, *matador::base_class<UserDirectory>( this ) ); field::process( op, *matador::base_class<UserDirectory>( this ) );
field::attribute( op, "schema_base_dn", schema_base_dn, 511 ); field::attribute( op, "schema_base_dn", schema_base_dn, VarChar511 );
field::attribute( op, "additional_user_base_dn", additional_user_base_dn, 511 ); field::attribute( op, "additional_user_base_dn", additional_user_base_dn, VarChar511 );
field::attribute( op, "additional_group_base_dn", additional_group_base_dn, 511 ); field::attribute( op, "additional_group_base_dn", additional_group_base_dn, VarChar511 );
field::belongs_to( op, "user_schema_settings", user_schema_settings, matador::utils::default_foreign_attributes ); field::belongs_to( op, "user_schema_settings", user_schema_settings, utils::CascadeAllFetchLazy );
field::belongs_to( op, "group_schema_settings", group_schema_settings, matador::utils::default_foreign_attributes ); field::belongs_to( op, "group_schema_settings", group_schema_settings, utils::CascadeAllFetchLazy );
field::belongs_to( op, "import_settings", import_settings, matador::utils::default_foreign_attributes ); field::belongs_to( op, "import_settings", import_settings, utils::CascadeAllFetchLazy );
field::has_many( op, "ldap_users", users, "users_id", matador::utils::default_foreign_attributes ); field::has_many( op, "ldap_users", users, "users_id", utils::CascadeAllFetchEager );
field::has_many( op, "ldap_groups", groups, "groups_id", matador::utils::default_foreign_attributes ); field::has_many( op, "ldap_groups", groups, "groups_id", utils::CascadeAllFetchEager );
} }
}; };
} }
+13 -12
View File
@@ -4,27 +4,28 @@
#include "../core/Model.hpp" #include "../core/Model.hpp"
#include "matador/utils/base_class.hpp" #include "matador/utils/base_class.hpp"
#include "matador/utils/foreign_attributes.hpp"
namespace work::models::admin { namespace work::models::admin {
struct LdapUserDirectory; struct LdapUserDirectory;
struct LdapUserSchemaSettings : core::Model { struct LdapUserSchemaSettings : core::Model {
std::string user_object_filter; std::string user_object_filter{};
std::string user_unique_id_attribute; std::string user_unique_id_attribute{};
std::string user_member_of_attribute; std::string user_member_of_attribute{};
std::string user_name_attribute; std::string user_name_attribute{};
matador::object::object_ptr<LdapUserDirectory> ldap_user_directory; matador::object::object_ptr<LdapUserDirectory> ldap_user_directory{};
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process( Operator& op ) {
namespace field = matador::access; using namespace matador;
namespace field = access;
field::process(op, *matador::base_class<Model>( this )); field::process(op, *matador::base_class<Model>( this ));
field::has_one(op, "ldap_user_directory", ldap_user_directory, matador::utils::default_foreign_attributes); field::has_one(op, "ldap_user_directory", ldap_user_directory, utils::CascadeAllFetchLazy);
field::attribute(op, "user_object_filter", user_object_filter, 511); field::attribute(op, "user_object_filter", user_object_filter, VarChar511);
field::attribute(op, "user_unique_id_attribute", user_unique_id_attribute, 511); field::attribute(op, "user_unique_id_attribute", user_unique_id_attribute, VarChar511);
field::attribute(op, "user_member_of_attribute", user_member_of_attribute, 511); field::attribute(op, "user_member_of_attribute", user_member_of_attribute, VarChar511);
field::attribute(op, "user_name_attribute", user_name_attribute, 511); field::attribute(op, "user_name_attribute", user_name_attribute, VarChar511);
} }
}; };
+15 -7
View File
@@ -9,8 +9,11 @@
#include "matador/object/object_ptr.hpp" #include "matador/object/object_ptr.hpp"
#include "matador/query/meta_table_macro.hpp"
#include "matador/utils/base_class.hpp" #include "matador/utils/base_class.hpp"
#include "matador/utils/enum_mapper.hpp" #include "matador/utils/enum_mapper.hpp"
#include "matador/utils/foreign_attributes.hpp"
namespace work::models::admin { namespace work::models::admin {
@@ -39,25 +42,30 @@ static const matador::utils::enum_mapper<FailReason> FailReasonEnum({
}); });
struct LoginHistory : core::Model { struct LoginHistory : core::Model {
matador::object::object_ptr<User> user; matador::object::object_ptr<User> client;
matador::object::object_ptr<Scenario> scenario; matador::object::object_ptr<Scenario> scenario;
matador::object::object_ptr<CollectionCenter> collection_center; matador::object::object_ptr<CollectionCenter> collection_center;
std::string login_name; std::string login_name;
FailReason fail_reason{FailReason::NoFailReason}; FailReason fail_reason{FailReason::NoFailReason};
core::timestamp login_time; matador::utils::timestamp_type_t login_time;
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process( Operator& op ) {
namespace field = matador::access; using namespace matador;
namespace field = access;
field::process_base<Model>( op, *this );
field::process( op, *matador::base_class<Model>( this ) ); field::process( op, *matador::base_class<Model>( this ) );
field::belongs_to( op, "user", user, matador::utils::default_foreign_attributes ); field::belongs_to( op, "client", client, utils::CascadeAllFetchLazy );
field::belongs_to( op, "scenario", scenario, matador::utils::default_foreign_attributes ); field::belongs_to( op, "scenario", scenario, utils::CascadeAllFetchLazy );
field::belongs_to( op, "collection_center", collection_center, matador::utils::default_foreign_attributes ); field::belongs_to( op, "collection_center", collection_center, utils::CascadeAllFetchLazy );
field::attribute( op, "login_name", login_name, 511 ); field::attribute( op, "login_name", login_name, VarChar511 );
field::attribute( op, "fail_reason", fail_reason ); field::attribute( op, "fail_reason", fail_reason );
field::attribute( op, "login_time", login_time ); field::attribute( op, "login_time", login_time );
} }
}; };
} }
META_TABLE( login_histories, LOGIN_HISTORY, id, version, client, scenario, collection_center, login_name, fail_reason, login_time )
#endif //LOGIN_HISTORY_HPP #endif //LOGIN_HISTORY_HPP
+6 -5
View File
@@ -40,20 +40,21 @@ struct Scenario : core::Model {
std::string name; std::string name;
ScenarioMode mode{ScenarioMode::InvalidMode}; ScenarioMode mode{ScenarioMode::InvalidMode};
std::string description; std::string description;
matador::utils::blob symbol; matador::utils::blob_type_t symbol;
ScenarioState state{ScenarioState::InvalidState}; ScenarioState state{ScenarioState::InvalidState};
matador::object::collection<matador::object::object_ptr<CollectionCenter>> collection_center; matador::object::collection<matador::object::object_ptr<CollectionCenter>> collection_center;
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process( Operator& op ) {
namespace field = matador::access; using namespace matador;
namespace field = access;
field::process( op, *matador::base_class<Model>( this ) ); field::process( op, *matador::base_class<Model>( this ) );
field::attribute( op, "name", name, 511 ); field::attribute( op, "name", name, UniqueVarChar511 );
field::attribute( op, "mode", mode ); field::attribute( op, "mode", mode );
field::attribute( op, "description", description, 511 ); field::attribute( op, "description", description, VarChar511 );
field::attribute( op, "symbol", symbol ); field::attribute( op, "symbol", symbol );
field::attribute( op, "state", state ); field::attribute( op, "state", state );
field::has_many( op, "collection_center", collection_center, "scenario_id", matador::utils::fetch_type::LAZY ); field::has_many( op, "collection_center", collection_center, "scenario_id", utils::CascadeAllFetchLazy );
} }
}; };
} }
+16 -10
View File
@@ -4,11 +4,13 @@
#include "LockType.hpp" #include "LockType.hpp"
#include "../core/Model.hpp" #include "../core/Model.hpp"
#include "../core/Types.hpp"
#include "matador/object/object_ptr.hpp" #include "matador/object/object_ptr.hpp"
#include "matador/query/meta_table_macro.hpp"
#include "matador/utils/base_class.hpp" #include "matador/utils/base_class.hpp"
#include "matador/utils/foreign_attributes.hpp"
#include "matador/utils/types.hpp" #include "matador/utils/types.hpp"
namespace work::models::admin { namespace work::models::admin {
@@ -16,30 +18,34 @@ struct UserDirectory;
struct User : core::Model { struct User : core::Model {
std::string name; std::string name;
matador::utils::blob symbol; matador::utils::blob_type_t symbol;
std::string salt; std::string salt;
std::string password; std::string password;
LockType lock_type{LockType::NoLock}; LockType lock_type{LockType::NoLock};
core::timestamp locked_at; matador::utils::timestamp_type_t locked_at;
std::string lock_reason; std::string lock_reason;
std::string role; std::string role;
matador::object::object_ptr<UserDirectory> user_directory; matador::object::object_ptr<UserDirectory> user_directory;
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process( Operator& op ) {
namespace field = matador::access; using namespace matador;
namespace field = access;
field::process( op, *matador::base_class<Model>( this ) ); field::process( op, *matador::base_class<Model>( this ) );
field::attribute( op, "name", name, 511 ); field::attribute( op, "name", name, UniqueVarChar511 );
field::attribute( op, "symbol", symbol ); field::attribute( op, "symbol", symbol );
field::attribute( op, "salt", salt, 511 ); field::attribute( op, "salt", salt, VarChar511 );
field::attribute( op, "password", password, 511 ); field::attribute( op, "password", password, VarChar511 );
field::attribute( op, "lock_type", lock_type ); field::attribute( op, "lock_type", lock_type );
field::attribute( op, "locked_at", locked_at ); field::attribute( op, "locked_at", locked_at );
field::attribute( op, "lock_reason", lock_reason, 511 ); field::attribute( op, "lock_reason", lock_reason, VarChar511 );
field::attribute( op, "role", role, 63 ); field::attribute( op, "role", role, VarChar63 );
field::belongs_to( op, "user_directory", user_directory, matador::utils::default_foreign_attributes ); field::belongs_to( op, "user_directory", user_directory, utils::CascadeAllFetchLazy );
} }
}; };
} }
META_TABLE(users, USER, id, version, name, symbol, salt, password, lock_type, locked_at, lock_reason, role, user_directory)
#endif //USER_HPP #endif //USER_HPP
+1 -1
View File
@@ -13,7 +13,7 @@ struct UserDirectory : core::Model {
void process( Operator& op ) { void process( Operator& op ) {
namespace field = matador::access; namespace field = matador::access;
field::process( op, *matador::base_class<Model>( this ) ); field::process( op, *matador::base_class<Model>( this ) );
field::attribute( op, "name", name, 511 ); field::attribute( op, "name", name, matador::VarChar511 );
} }
}; };
} }
+8 -7
View File
@@ -6,26 +6,27 @@
#include "User.hpp" #include "User.hpp"
#include "../core/Model.hpp" #include "../core/Model.hpp"
#include "../core/Types.hpp"
#include "matador/object/object_ptr.hpp" #include "matador/object/object_ptr.hpp"
#include "matador/utils/base_class.hpp" #include "matador/utils/base_class.hpp"
#include "matador/utils/foreign_attributes.hpp"
namespace work::models::admin { namespace work::models::admin {
struct UserSession : core::Model { struct UserSession : core::Model {
matador::object::object_ptr<User> user; matador::object::object_ptr<User> client;
matador::object::object_ptr<Scenario> scenario; matador::object::object_ptr<Scenario> scenario;
matador::object::object_ptr<CollectionCenter> collection_center; matador::object::object_ptr<CollectionCenter> collection_center;
core::timestamp offline_since; matador::utils::timestamp_type_t offline_since;
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process( Operator& op ) {
namespace field = matador::access; using namespace matador;
namespace field = access;
field::process( op, *matador::base_class<Model>( this ) ); field::process( op, *matador::base_class<Model>( this ) );
field::belongs_to( op, "user", user, matador::utils::default_foreign_attributes ); field::belongs_to( op, "client", client, utils::CascadeAllFetchLazy );
field::belongs_to( op, "scenario", scenario, matador::utils::default_foreign_attributes ); field::belongs_to( op, "scenario", scenario, utils::CascadeAllFetchLazy );
field::belongs_to( op, "collection_center", collection_center, matador::utils::default_foreign_attributes ); field::belongs_to( op, "collection_center", collection_center, utils::CascadeAllFetchLazy );
field::attribute( op, "offline_since", offline_since ); field::attribute( op, "offline_since", offline_since );
} }
}; };
+2 -2
View File
@@ -7,8 +7,8 @@
namespace work::core { namespace work::core {
struct Model { struct Model {
uint64_t id; int64_t id{};
uint64_t version; uint64_t version{};
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process( Operator& op ) {
-9
View File
@@ -1,9 +0,0 @@
#ifndef TYPES_HPP
#define TYPES_HPP
#include <chrono>
namespace work::core {
using timestamp = std::chrono::system_clock::time_point;
}
#endif //TYPES_HPP
+4 -2
View File
@@ -2,6 +2,7 @@
#define USERINFO_HPP #define USERINFO_HPP
#include "matador/utils/access.hpp" #include "matador/utils/access.hpp"
#include "matador/utils/field_attributes.hpp"
namespace work::core { namespace work::core {
struct UserInfo { struct UserInfo {
@@ -12,9 +13,10 @@ struct UserInfo {
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process( Operator& op ) {
namespace field = matador::access; namespace field = matador::access;
using namespace matador;
field::attribute( op, "user_session_id", user_session_id ); field::attribute( op, "user_session_id", user_session_id );
field::attribute( op, "user_role", user_role, 255 ); field::attribute( op, "user_role", user_role, VarChar255 );
field::attribute( op, "database_name", database_name, 255 ); field::attribute( op, "database_name", database_name, VarChar255 );
} }
}; };
} }
+5 -1
View File
@@ -5,6 +5,8 @@
#include "matador/object/collection.hpp" #include "matador/object/collection.hpp"
#include "matador/query/meta_table_macro.hpp"
#include "matador/utils/foreign_attributes.hpp" #include "matador/utils/foreign_attributes.hpp"
namespace work::models::jobs { namespace work::models::jobs {
@@ -14,9 +16,11 @@ struct IdListPayload : Payload {
void process( Operator& op ) { void process( Operator& op ) {
namespace field = matador::access; namespace field = matador::access;
field::process( op, *matador::base_class<Payload>( this ) ); field::process( op, *matador::base_class<Payload>( this ) );
field::has_many( op, "payload_ids", payload_ids, "payload_id", matador::utils::default_foreign_attributes ); field::has_many( op, "payload_ids", payload_ids, "payload_id", matador::utils::CascadeNoneFetchLazy );
} }
}; };
} }
META_TABLE(id_list_payloads, ID_LIST_PAYLOAD, id, version, type, job)
#endif //ID_LIST_PAYLOAD_HPP #endif //ID_LIST_PAYLOAD_HPP
+4
View File
@@ -3,6 +3,8 @@
#include "Payload.hpp" #include "Payload.hpp"
#include "matador/query/meta_table_macro.hpp"
namespace work::models::jobs { namespace work::models::jobs {
struct IdPayload : Payload { struct IdPayload : Payload {
uint64_t payload_id{}; uint64_t payload_id{};
@@ -15,4 +17,6 @@ struct IdPayload : Payload {
}; };
} }
META_TABLE(id_payloads, ID_PAYLOAD, id, version, type, job, payload_id)
#endif //ID_PAYLOAD_HPP #endif //ID_PAYLOAD_HPP
+11 -6
View File
@@ -7,12 +7,14 @@
#include "Task.hpp" #include "Task.hpp"
#include "../core/Model.hpp" #include "../core/Model.hpp"
#include "../core/Types.hpp"
#include "../core/UserInfo.hpp" #include "../core/UserInfo.hpp"
#include "matador/object/object_ptr.hpp" #include "matador/object/object_ptr.hpp"
#include "matador/query/meta_table_macro.hpp"
#include "matador/utils/base_class.hpp" #include "matador/utils/base_class.hpp"
#include "matador/utils/foreign_attributes.hpp"
namespace work::models::jobs { namespace work::models::jobs {
@@ -21,26 +23,29 @@ struct Job : core::Model {
std::string description; std::string description;
JobState state; JobState state;
JobMode mode; JobMode mode;
core::timestamp created_at; matador::utils::timestamp_type_t created_at;
matador::object::object_ptr<Payload> payload; matador::object::object_ptr<Payload> payload;
matador::object::object_ptr<Task> task; matador::object::object_ptr<Task> task;
core::UserInfo user_info; core::UserInfo user_info;
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process( Operator& op ) {
using namespace matador;
namespace field = matador::access; namespace field = matador::access;
field::process( op, *matador::base_class<Model>( this ) ); field::process( op, *matador::base_class<Model>( this ) );
field::attribute( op, "name", name, 511 ); field::attribute( op, "name", name, UniqueVarChar511 );
field::attribute( op, "description", description, 511 ); field::attribute( op, "description", description, VarChar1023 );
field::attribute( op, "state", state ); field::attribute( op, "state", state );
field::attribute( op, "mode", mode ); field::attribute( op, "mode", mode );
field::attribute( op, "created_at", created_at ); field::attribute( op, "created_at", created_at );
field::has_one(op, "payload", payload, matador::utils::default_foreign_attributes ); field::has_one(op, "payload", payload, utils::CascadeAllFetchLazy );
field::belongs_to( op, "task", task, matador::utils::default_foreign_attributes ); field::belongs_to( op, "task", task, utils::CascadeAllFetchLazy );
field::attribute( op, "user_info", user_info ); field::attribute( op, "user_info", user_info );
} }
}; };
} }
META_TABLE(jobs, JOB, id, version, name, description, state, mode, created_at, payload, task, user_info)
#endif //JOB_HPP #endif //JOB_HPP
+10 -4
View File
@@ -3,22 +3,28 @@
#include "../core/Model.hpp" #include "../core/Model.hpp"
#include "matador/query/meta_table_macro.hpp"
#include "matador/utils/base_class.hpp" #include "matador/utils/base_class.hpp"
#include "matador/utils/foreign_attributes.hpp"
namespace work::models::jobs { namespace work::models::jobs {
struct Job; struct Job;
struct Payload : core::Model { struct Payload : core::Model {
std::string type; std::string type{};
matador::object::object_ptr<Job> job; matador::object::object_ptr<Job> job{};
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process( Operator& op ) {
using namespace matador;
namespace field = matador::access; namespace field = matador::access;
field::process( op, *matador::base_class<Model>( this ) ); field::process( op, *matador::base_class<Model>( this ) );
field::attribute( op, "type", type, 255 ); field::attribute( op, "type", type, VarChar255 );
field::belongs_to( op, "job", job, matador::utils::default_foreign_attributes ); field::belongs_to( op, "job", job, utils::CascadeAllFetchLazy );
} }
}; };
} }
META_TABLE(payloads, PAYLOAD, id, version, type, job)
#endif //PAYLOAD_HPP #endif //PAYLOAD_HPP
+12 -7
View File
@@ -4,11 +4,13 @@
#include "TaskState.hpp" #include "TaskState.hpp"
#include "../core/Model.hpp" #include "../core/Model.hpp"
#include "../core/Types.hpp"
#include "matador/object/object_ptr.hpp" #include "matador/object/object_ptr.hpp"
#include "matador/query/meta_table_macro.hpp"
#include "matador/utils/base_class.hpp" #include "matador/utils/base_class.hpp"
#include "matador/utils/foreign_attributes.hpp"
namespace work::models::jobs { namespace work::models::jobs {
struct Task : core::Model { struct Task : core::Model {
@@ -18,19 +20,20 @@ struct Task : core::Model {
TaskState state; TaskState state;
matador::object::object_ptr<Payload> payload; matador::object::object_ptr<Payload> payload;
JobMode job_mode; JobMode job_mode;
core::timestamp start_delay; matador::utils::timestamp_type_t start_delay;
core::timestamp interval; matador::utils::timestamp_type_t interval;
uint64_t user_session_id; uint64_t user_session_id;
template<typename Operator> template<typename Operator>
void process( Operator& op ) { void process( Operator& op ) {
using namespace matador;
namespace field = matador::access; namespace field = matador::access;
field::process( op, *matador::base_class<Model>( this ) ); field::process( op, *matador::base_class<Model>( this ) );
field::attribute( op, "name", name, 511 ); field::attribute( op, "name", name, UniqueVarChar511 );
field::attribute( op, "description", description, 511 ); field::attribute( op, "description", description, VarChar1023 );
field::attribute( op, "job_name", job_name, 511 ); field::attribute( op, "job_name", job_name, VarChar511 );
field::attribute( op, "state", state ); field::attribute( op, "state", state );
field::belongs_to( op, "payload", payload, matador::utils::default_foreign_attributes ); field::belongs_to( op, "payload", payload, utils::CascadeAllFetchLazy );
field::attribute( op, "job_mode", job_mode ); field::attribute( op, "job_mode", job_mode );
field::attribute( op, "start_delay", start_delay ); field::attribute( op, "start_delay", start_delay );
field::attribute( op, "interval", interval ); field::attribute( op, "interval", interval );
@@ -39,4 +42,6 @@ struct Task : core::Model {
}; };
} }
META_TABLE(tasks, TASK, id, version, name, description, job_name, state, payload, job_mode, start_delay, interval, user_session_id)
#endif //TASK_HPP #endif //TASK_HPP
+1 -1
View File
@@ -101,7 +101,7 @@ public:
void clear(); void clear();
private: private:
void get_time_stamp(char* timestamp_buffer) const; static void get_time_stamp(char* timestamp_buffer);
private: private:
static std::map<log_level, std::string> level_strings; static std::map<log_level, std::string> level_strings;
+11 -13
View File
@@ -8,15 +8,14 @@ namespace matador::logger {
/** /**
* Represents all available log levels * Represents all available log levels
*/ */
enum class log_level enum class log_level {
{ Fatal, /**< If a serious error occurred, use FATAL level */
LVL_FATAL, /**< If a serious error occurred, use FATAL level */ Error, /**< On error use ERROR level */
LVL_ERROR, /**< On error use ERROR level */ Warn, /**< Warnings should use WARN level */
LVL_WARN, /**< Warnings should use WARN level */ Info, /**< Information should go with INFO level */
LVL_INFO, /**< Information should go with INFO level */ Debug, /**< Debug output should use DEBUG level */
LVL_DEBUG, /**< Debug output should use DEBUG level */ Trace, /**< Trace information should use TRACE level */
LVL_TRACE, /**< Trace information should use TRACE level */ All /**< This level represents all log levels and should be used for logging */
LVL_ALL /**< This level represents all log levels and should be used for logging */
}; };
/** /**
@@ -31,10 +30,9 @@ std::ostream& operator<<(std::ostream &os, log_level lvl);
/// @cond MATADOR_DEV /// @cond MATADOR_DEV
struct log_level_range struct log_level_range {
{ log_level min_level = log_level::Fatal;
log_level min_level = log_level::LVL_INFO; log_level max_level = log_level::Info;
log_level max_level = log_level::LVL_FATAL;
}; };
/// @endcond /// @endcond
+7 -13
View File
@@ -123,11 +123,8 @@ public:
protected: protected:
/// @cond MATADOR_DEV /// @cond MATADOR_DEV
log_manager() log_manager();
{ /// @endcond
default_log_domain_ = log_domain_map_.insert(std::make_pair("default", std::make_shared<log_domain>("default", default_log_level_range_))).first->second;
}
/// @endcond
private: private:
std::shared_ptr<log_domain> acquire_domain(const std::string &name); std::shared_ptr<log_domain> acquire_domain(const std::string &name);
@@ -140,6 +137,8 @@ private:
std::map<std::string, std::shared_ptr<log_domain>> log_domain_map_; std::map<std::string, std::shared_ptr<log_domain>> log_domain_map_;
static log_level_range default_log_level_range_; static log_level_range default_log_level_range_;
std::mutex mutex_;
}; };
/** /**
@@ -285,15 +284,10 @@ void log_default(log_level lvl, const std::string &source, const char *message);
* @param args The arguments for the message * @param args The arguments for the message
*/ */
template<typename... ARGS> template<typename... ARGS>
void log(const log_level lvl, const std::string &source, const char *what, ARGS const &... args) void log(const log_level lvl, const std::string &source, const char *what, ARGS const &... args) {
{ char message_buffer[logger::buffer_size];
char message_buffer[16384];
#ifdef _MSC_VER snprintf(message_buffer, logger::buffer_size, what, args...);
sprintf_s(message_buffer, 912, what, args...);
#else
sprintf(message_buffer, what, args...);
#endif
log_default(lvl, source, message_buffer); log_default(lvl, source, message_buffer);
} }
+11 -16
View File
@@ -9,7 +9,6 @@
#include <mutex> #include <mutex>
namespace matador::logger { namespace matador::logger {
/** /**
* @brief logger to write log messages to log domains * @brief logger to write log messages to log domains
* *
@@ -31,9 +30,9 @@ namespace matador::logger {
* All log messages are written through the internal * All log messages are written through the internal
* log_domain object to the sinks. * log_domain object to the sinks.
*/ */
class logger final class logger final {
{
public: public:
static constexpr size_t buffer_size = 16384;
/** /**
* Create a logger with a given source name connected * Create a logger with a given source name connected
@@ -69,7 +68,7 @@ public:
* @param args The arguments to be replaced in the message * @param args The arguments to be replaced in the message
*/ */
template<typename ... ARGS> template<typename ... ARGS>
void fatal(const char *what, ARGS const &... args) const { log(log_level::LVL_FATAL, what, args...); } void fatal(const char *what, ARGS const &... args) const { log(log_level::Fatal, what, args...); }
/** /**
* Writes a log message string with log level LVL_FATAL * Writes a log message string with log level LVL_FATAL
@@ -91,7 +90,7 @@ public:
* @param args The arguments to be replaced in the message * @param args The arguments to be replaced in the message
*/ */
template<typename ... ARGS> template<typename ... ARGS>
void error(const char *what, ARGS const &... args) const { log(log_level::LVL_ERROR, what, args...); } void error(const char *what, ARGS const &... args) const { log(log_level::Error, what, args...); }
/** /**
* Writes a log message string with log level LVL_FATAL * Writes a log message string with log level LVL_FATAL
@@ -113,7 +112,7 @@ public:
* @param args The arguments to be replaced in the message * @param args The arguments to be replaced in the message
*/ */
template<typename ... ARGS> template<typename ... ARGS>
void warn(const char *what, ARGS const &... args) const { log(log_level::LVL_WARN, what, args...); } void warn(const char *what, ARGS const &... args) const { log(log_level::Warn, what, args...); }
/** /**
* Writes a log message string with log level LVL_FATAL * Writes a log message string with log level LVL_FATAL
@@ -135,7 +134,7 @@ public:
* @param args The arguments to be replaced in the message * @param args The arguments to be replaced in the message
*/ */
template<typename ... ARGS> template<typename ... ARGS>
void info(const char *what, ARGS const &... args) const { log(log_level::LVL_INFO, what, args...); } void info(const char *what, ARGS const &... args) const { log(log_level::Info, what, args...); }
/** /**
* Writes a log message string with log level LVL_FATAL * Writes a log message string with log level LVL_FATAL
@@ -157,7 +156,7 @@ public:
* @param args The arguments to be replaced in the message * @param args The arguments to be replaced in the message
*/ */
template<typename ... ARGS> template<typename ... ARGS>
void debug(const char *what, ARGS const &... args) const { log(log_level::LVL_DEBUG, what, args...); } void debug(const char *what, ARGS const &... args) const { log(log_level::Debug, what, args...); }
/** /**
* Writes a log message string with log level LVL_FATAL * Writes a log message string with log level LVL_FATAL
@@ -179,7 +178,7 @@ public:
* @param args The arguments to be replaced in the message * @param args The arguments to be replaced in the message
*/ */
template<typename ... ARGS> template<typename ... ARGS>
void trace(const char *what, ARGS const &... args) const { log(log_level::LVL_TRACE, what, args...); } void trace(const char *what, ARGS const &... args) const { log(log_level::Trace, what, args...); }
/** /**
* Writes a log message represented by a char pointer * Writes a log message represented by a char pointer
@@ -222,14 +221,10 @@ private:
}; };
template<typename... ARGS> template<typename... ARGS>
void logger::log(log_level lvl, const char *what, ARGS const &... args) const { void logger::log(const log_level lvl, const char *what, ARGS const &... args) const {
char message_buffer[16384]; char message_buffer[buffer_size];
#ifdef _MSC_VER snprintf(message_buffer, buffer_size, what, args...);
sprintf_s(message_buffer, 16384, what, args...);
#else
sprintf(message_buffer, what, args...);
#endif
logger_domain_->log(lvl, source_, message_buffer); logger_domain_->log(lvl, source_, message_buffer);
} }
-2
View File
@@ -1,8 +1,6 @@
#ifndef MATADOR_NET_OS_HPP #ifndef MATADOR_NET_OS_HPP
#define MATADOR_NET_OS_HPP #define MATADOR_NET_OS_HPP
#include "matador/net/export.hpp"
#include <cstdio> #include <cstdio>
#if _WIN32 #if _WIN32
@@ -0,0 +1,23 @@
#ifndef MATADOR_ABSTRACT_COLLECTION_RESOLVER_HPP
#define MATADOR_ABSTRACT_COLLECTION_RESOLVER_HPP
#include "matador/object/abstract_type_resolver.hpp"
#include <string>
#include <typeindex>
namespace matador::object {
class abstract_joined_resolver : public abstract_type_resolver {
public:
[[nodiscard]] const std::type_index& root_type() const;
[[nodiscard]] const std::string& collection_name() const;
protected:
explicit abstract_joined_resolver(const std::type_index& root_type, const std::type_index& type, std::string collection_name);
public:
const std::type_index root_type_;
const std::string collection_name_;
};
}
#endif // MATADOR_ABSTRACT_COLLECTION_RESOLVER_HPP
@@ -0,0 +1,20 @@
#ifndef MATADOR_ABSTRACT_TYPE_RESOLVER_HPP
#define MATADOR_ABSTRACT_TYPE_RESOLVER_HPP
#include <typeindex>
namespace matador::object {
class abstract_type_resolver {
public:
virtual ~abstract_type_resolver() = default;
[[nodiscard]] const std::type_index& type() const { return type_; }
protected:
explicit abstract_type_resolver(const std::type_index& ti) : type_(ti) {}
public:
const std::type_index type_;
};
}
#endif //MATADOR_ABSTRACT_TYPE_RESOLVER_HPP
@@ -0,0 +1,17 @@
#ifndef MATADOR_ABSTRACT_TYPE_RESOLVER_FACTORY_HPP
#define MATADOR_ABSTRACT_TYPE_RESOLVER_FACTORY_HPP
#include "matador/object/abstract_type_resolver.hpp"
#include <memory>
namespace matador::object {
class abstract_type_resolver_factory {
public:
virtual ~abstract_type_resolver_factory() = default;
[[nodiscard]] virtual std::shared_ptr<abstract_type_resolver> acquire_object_resolver(const std::type_index &type) const = 0;
virtual void register_object_resolver(std::shared_ptr<abstract_type_resolver> &&resolver) = 0;
};
}
#endif //MATADOR_ABSTRACT_TYPE_RESOLVER_FACTORY_HPP
+81
View File
@@ -0,0 +1,81 @@
#ifndef QUERY_COLUMN_DEFINITION_HPP
#define QUERY_COLUMN_DEFINITION_HPP
#include "matador/utils/basic_types.hpp"
#include "matador/utils/default_type_traits.hpp"
#include "matador/utils/field_attributes.hpp"
#include <memory>
#include <ostream>
namespace matador::object {
enum class null_option_type : uint8_t {
Nullable, NotNull
};
class object;
class attribute {
public:
explicit attribute(std::string name); // NOLINT(*-explicit-constructor)
attribute(const attribute&) = default;
attribute& operator=(const attribute&) = default;
attribute(attribute&&) noexcept = default;
attribute& operator=(attribute&&) noexcept = default;
attribute() = default;
attribute(std::string name,
utils::basic_type type,
const utils::field_attributes &attr = NullAttributes,
null_option_type null_opt = null_option_type::NotNull);
[[nodiscard]] const std::string& name() const;
void name(const std::string& n);
[[nodiscard]] std::string full_name() const;
[[nodiscard]] size_t index() const;
[[nodiscard]] const utils::field_attributes& attributes() const;
[[nodiscard]] utils::field_attributes& attributes();
[[nodiscard]] bool is_nullable() const;
[[nodiscard]] utils::basic_type type() const;
[[nodiscard]] std::shared_ptr<object> owner() const;
void change_type(utils::basic_type type, const utils::field_attributes &attr = NullAttributes);
template < typename Type >
void change_type(const utils::field_attributes &attr = NullAttributes) {
type_ = utils::data_type_traits<Type>::type(attr.size());
}
[[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_timestamp() const;
[[nodiscard]] bool is_blob() const;
[[nodiscard]] bool is_null() const;
template< typename Type >
[[nodiscard]] bool is_type_of() const {
return type() == utils::data_type_traits<Type>::type(attributes().size());
}
friend std::ostream& operator<<(std::ostream& os, const attribute& attr);
private:
friend class object;
friend class object_generator;
std::string name_;
size_t index_{0};
std::weak_ptr<object> owner_;
utils::basic_type type_{utils::basic_type::Null};
utils::field_attributes options_{};
null_option_type null_option_{null_option_type::NotNull};
};
}
#endif //QUERY_COLUMN_DEFINITION_HPP
@@ -1,208 +0,0 @@
#ifndef QUERY_COLUMN_DEFINITION_HPP
#define QUERY_COLUMN_DEFINITION_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 <memory>
#include <vector>
namespace matador::object {
enum class null_option : uint8_t {
NULLABLE, NOT_NULL
};
class attribute_definition {
public:
explicit attribute_definition(const char *name); // NOLINT(*-explicit-constructor)
explicit attribute_definition(std::string name); // NOLINT(*-explicit-constructor)
attribute_definition(const attribute_definition&) = default;
attribute_definition& operator=(const attribute_definition&) = default;
attribute_definition(attribute_definition&&) noexcept = default;
attribute_definition& operator=(attribute_definition&&) noexcept = default;
attribute_definition() = default;
template<typename Type>
explicit attribute_definition(std::string name, std::string table_name, const utils::field_attributes& attr)
: attribute_definition(std::move(name), std::move(table_name), utils::data_type_traits<Type>::type(attr.size()), attr)
{}
attribute_definition(std::string name, std::string table_name, utils::basic_type type, const utils::field_attributes& attr);
template<typename Type>
explicit attribute_definition(std::string name, const utils::field_attributes& attr)
: attribute_definition(std::move(name), utils::data_type_traits<Type>::type(attr.size()), attr)
{}
template<typename Type>
attribute_definition(std::string name, const Type &, const utils::field_attributes& attr, null_option null_opt)
: attribute_definition(std::move(name), utils::data_type_traits<Type>::type(attr.size()), attr, null_opt)
{}
template<size_t SIZE>
attribute_definition(std::string name, const char (&)[SIZE], const utils::field_attributes& attr, const null_option null_opt)
: attribute_definition(std::move(name), utils::data_type_traits<const char*>::type(attr.size()), attr, null_opt)
{}
attribute_definition(std::string name, utils::basic_type type, const utils::field_attributes&, null_option null_opt, size_t index = 0);
attribute_definition(std::string name, std::string table_name, utils::basic_type type, const utils::field_attributes&, null_option null_opt, size_t index = 0);
template<typename Type>
attribute_definition(std::string name, const std::shared_ptr<attribute_definition> &ref_column, const utils::field_attributes& attr, null_option null_opt)
: attribute_definition(std::move(name), utils::data_type_traits<Type>::type(attr.size()), ref_column, attr, null_opt)
{}
attribute_definition(std::string name, utils::basic_type type, size_t index, const std::shared_ptr<attribute_definition> &ref_column, const utils::field_attributes& attr, null_option null_opt);
[[nodiscard]] const std::string& name() const;
void name(const std::string& n);
[[nodiscard]] std::string full_name() const;
[[nodiscard]] std::string table_name() const;
void table_name(const std::string& tn);
[[nodiscard]] int index() const;
[[nodiscard]] const utils::field_attributes& attributes() const;
[[nodiscard]] utils::field_attributes& attributes();
[[nodiscard]] bool is_nullable() const;
[[nodiscard]] utils::basic_type type() const;
[[nodiscard]] std::shared_ptr<attribute_definition> reference_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;
void type(utils::basic_type type);
template< typename Type >
[[nodiscard]] bool is_type_of() const {
return std::holds_alternative<Type>(value_);
}
[[nodiscard]] std::string str() const;
template<typename Type>
void set(const Type &value, const utils::field_attributes &attr = utils::null_attributes)
{
attributes_ = attr;
value_ = value;
}
void set(const std::string &value, const utils::field_attributes &attr)
{
value_ = value;
attributes_ = attr;
}
void set(const char *value, const utils::field_attributes &attr)
{
value_ = std::string(value);
attributes_ = attr;
}
template<class Type>
std::optional<Type> as() const
{
return value_.as<Type>();
}
friend std::ostream& operator<<(std::ostream &out, const attribute_definition &col);
private:
template<class Operator>
void process(Operator &op)
{
op.on_attribute(name_.c_str(), value_, attributes_);
}
using data_type_index = std::vector<utils::basic_type>;
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};
utils::value value_;
std::shared_ptr<attribute_definition> reference_column_;
};
/**
* User defined literal to have a shortcut creating a column object
* @param name Name of the column
* @param type
* @param attr Length of the column name
* @param null_opt
* @return A column object with a given name
*/
attribute_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 >
attribute_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, utils::data_type_traits<Type>::type(0), attr, null_opt);
}
template <>
attribute_definition make_column<std::string>(const std::string &name, utils::field_attributes attr, null_option null_opt);
template < typename Type >
attribute_definition make_pk_column(const std::string &name, size_t size = 0)
{
return make_column<Type>(name, { size, utils::constraints::PRIMARY_KEY });
}
template <>
attribute_definition make_pk_column<std::string>(const std::string &name, size_t size);
template < typename Type >
attribute_definition make_fk_column(const std::string &name, size_t size, const std::shared_ptr<attribute_definition> &ref_column) {
return {name, utils::data_type_traits<Type>::type(size), ref_column, { size, utils::constraints::FOREIGN_KEY }};
}
template < typename Type >
[[maybe_unused]] attribute_definition make_fk_column(const std::string &name, const std::shared_ptr<attribute_definition> &ref_column)
{
return {name, utils::data_type_traits<Type>::type(0), 0, ref_column, { 0, utils::constraints::FOREIGN_KEY }, null_option::NOT_NULL};
}
template <>
[[maybe_unused]] attribute_definition make_fk_column<std::string>(const std::string &name, size_t size, const std::shared_ptr<attribute_definition> &ref_column);
template < typename Type >
[[maybe_unused]] attribute_definition make_fk_column(const std::string &name, size_t size, const std::string &ref_table_name, const std::string &ref_column_name) {
return {
name, utils::data_type_traits<Type>::type(size), 0,
std::make_shared<attribute_definition>(ref_column_name, ref_table_name, utils::data_type_traits<Type>::type(size), utils::constraints::FOREIGN_KEY),
{ 0, utils::constraints::FOREIGN_KEY }, null_option::NOT_NULL
};
}
template < typename Type >
[[maybe_unused]] attribute_definition make_fk_column(const std::string &name, const std::string &ref_table_name, const std::string &ref_column_name) {
return {
name, utils::data_type_traits<Type>::type(0), 0,
std::make_shared<attribute_definition>(ref_column_name, ref_table_name, utils::data_type_traits<Type>::type(0), utils::constraints::FOREIGN_KEY),
{ 0, utils::constraints::FOREIGN_KEY }, null_option::NOT_NULL
};
}
template <>
[[maybe_unused]] attribute_definition make_fk_column<std::string>(const std::string &name, const std::string &ref_table_name, const std::string &ref_column_name);
template <>
[[maybe_unused]] attribute_definition make_fk_column<std::string>(const std::string &name, size_t size, const std::string &ref_table_name, const std::string &ref_column_name);
}
#endif //QUERY_COLUMN_DEFINITION_HPP
@@ -1,153 +0,0 @@
#ifndef QUERY_COLUMN_DEFINITION_GENERATOR_HPP
#define QUERY_COLUMN_DEFINITION_GENERATOR_HPP
#include "matador/object/attribute_definition.hpp"
#include "matador/utils/access.hpp"
#include "matador/utils/data_type_traits.hpp"
#include "matador/utils/error.hpp"
#include "matador/utils/field_attributes.hpp"
#include "matador/utils/primary_key_attribute.hpp"
#include <memory>
#include <typeindex>
#include <vector>
namespace matador::object {
class schema;
class fk_attribute_generator
{
public:
fk_attribute_generator() = default;
template<class Type>
attribute_definition generate(const char *id, Type &x, const std::shared_ptr<attribute_definition> &ref_column) {
access::process(*this, x);
return attribute_definition{id, type_, 0, ref_column, {utils::constraints::FOREIGN_KEY }, null_option::NOT_NULL};
}
template<typename ValueType>
void on_primary_key(const char *, ValueType &/*pk*/, const utils::primary_key_attribute& attr = utils::default_pk_attributes) {
type_ = utils::data_type_traits<ValueType>::type(attr.size());
}
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
template < class Type >
static void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
static void on_attribute(const char * /*id*/, char * /*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
template<class Pointer>
static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
template<class Pointer>
static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType>
static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType>
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType>
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const utils::foreign_attributes &/*attr*/) {}
private:
utils::basic_type type_{};
};
class attribute_definition_generator final {
private:
attribute_definition_generator(std::vector<attribute_definition> &columns, const schema &repo);
public:
~attribute_definition_generator() = default;
template < class Type >
static std::vector<attribute_definition> generate(const schema &repo)
{
std::vector<attribute_definition> columns;
attribute_definition_generator gen(columns, repo);
Type obj;
access::process(gen, obj);
return columns;
}
template < class Type >
static std::vector<attribute_definition> generate(const Type& obj, const schema &repo) {
std::vector<attribute_definition> columns;
attribute_definition_generator gen(columns, repo);
access::process(gen, obj);
return columns;
}
template < class Type >
void on_primary_key(const char *, Type &x, const utils::primary_key_attribute& attr = utils::default_pk_attributes);
void on_revision(const char *id, uint64_t &rev);
template<typename Type>
void on_attribute(const char *id, Type &x, const utils::field_attributes &attr = utils::null_attributes);
template<typename Type>
void on_attribute(const char *id, std::optional<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*/) {
on_foreign_key(id, x);
}
template<class Pointer>
void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {
// on_foreign_key(id, x);
}
template <class Pointer>
void on_foreign_key(const char *id, Pointer &x) {
std::shared_ptr<attribute_definition> ref_column;
std::type_index ti = typeid(typename Pointer::value_type);
if (const auto result = determine_foreign_ref(std::type_index(ti))) {
ref_column = *result;
} else {
ref_column = std::make_shared<attribute_definition>();
insert_missing_reference_column(ti, ref_column);
}
if (x.empty()) {
typename Pointer::value_type temp_val;
columns_.push_back(fk_column_generator_.generate(id, temp_val, ref_column));
} else {
columns_.push_back(fk_column_generator_.generate(id, *x, ref_column));
}
}
template<class ContainerType>
static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType>
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType>
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const utils::foreign_attributes &/*attr*/) {}
private:
[[nodiscard]] utils::result<std::shared_ptr<attribute_definition>, utils::error> determine_foreign_ref(const std::type_index &ti) const;
void insert_missing_reference_column(const std::type_index &ti, std::shared_ptr<attribute_definition> ref_column) const;
private:
size_t index_ = 0;
std::vector<attribute_definition> &columns_;
const schema &repo_;
fk_attribute_generator fk_column_generator_;
};
template<typename ValueType>
void attribute_definition_generator::on_primary_key(const char *id, ValueType &x, const utils::primary_key_attribute& attr) {
on_attribute(id, x, { attr.size(), utils::constraints::PRIMARY_KEY });
}
template<typename Type>
void attribute_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<typename Type>
void attribute_definition_generator::on_attribute(const char *id, std::optional<Type> & /*x*/, const utils::field_attributes &attr)
{
columns_.emplace_back(id, utils::data_type_traits<Type>::type(attr.size()), attr, null_option::NULLABLE);
}
}
#endif //QUERY_COLUMN_DEFINITION_GENERATOR_HPP
+16 -11
View File
@@ -1,17 +1,19 @@
#ifndef BASIC_PROTOTYPE_INFO_HPP #ifndef BASIC_PROTOTYPE_INFO_HPP
#define BASIC_PROTOTYPE_INFO_HPP #define BASIC_PROTOTYPE_INFO_HPP
#include "matador/object/object_definition.hpp" #include "matador/object/attribute.hpp"
#include "matador/object/restriction.hpp"
#include "matador/object/relation_endpoint.hpp" #include "matador/object/relation_endpoint.hpp"
#include "matador/utils/identifier.hpp" #include "matador/utils/identifier.hpp"
#include <list>
#include <string> #include <string>
#include <typeindex> #include <typeindex>
namespace matador::object { namespace matador::object {
class schema_node; class repository_node;
class relation_endpoint; class relation_endpoint;
class basic_object_info { class basic_object_info {
@@ -24,11 +26,18 @@ public:
[[nodiscard]] std::type_index type_index() const; [[nodiscard]] std::type_index type_index() const;
[[nodiscard]] std::string name() const; [[nodiscard]] std::string name() const;
[[nodiscard]] const object_definition& definition() const; [[nodiscard]] std::shared_ptr<class object> object() const;
[[nodiscard]] std::shared_ptr<attribute_definition> reference_column() const; [[nodiscard]] const std::vector<attribute>& attributes() const;
[[nodiscard]] const std::list<restriction>& constraints() const;
[[nodiscard]] bool has_primary_key() const; [[nodiscard]] bool has_primary_key() const;
[[nodiscard]] const utils::identifier& primary_key() const; [[nodiscard]] const utils::identifier& primary_key() const;
[[nodiscard]] const attribute* primary_key_attribute() const;
void update_name(const std::string& name) const;
virtual void on_attach() const = 0;
virtual void on_detach() const = 0;
endpoint_iterator register_relation_endpoint(const std::type_index &type, const std::shared_ptr<relation_endpoint> &endpoint); endpoint_iterator register_relation_endpoint(const std::type_index &type, const std::shared_ptr<relation_endpoint> &endpoint);
void unregister_relation_endpoint(const std::type_index &type); void unregister_relation_endpoint(const std::type_index &type);
@@ -49,15 +58,11 @@ public:
[[nodiscard]] bool endpoints_empty() const; [[nodiscard]] bool endpoints_empty() const;
protected: protected:
basic_object_info(std::shared_ptr<schema_node> node, utils::identifier &&pk, const std::shared_ptr<attribute_definition> &pk_column, object_definition &&definition); basic_object_info(const repository_node& node, const std::shared_ptr<class object> &obj);
basic_object_info(std::shared_ptr<schema_node> node, utils::identifier &&pk, const std::shared_ptr<attribute_definition> &pk_column);
basic_object_info(std::shared_ptr<schema_node> node, object_definition &&definition);
protected: protected:
std::shared_ptr<schema_node> node_; /**< prototype node of the represented object type */ std::shared_ptr<class object> object_;
object_definition definition_; const repository_node& node_; /**< prototype node of the represented object type */
std::optional<utils::identifier> identifier_;
std::shared_ptr<attribute_definition> pk_column_;
t_endpoint_map relation_endpoints_; t_endpoint_map relation_endpoints_;
}; };
+156
View File
@@ -0,0 +1,156 @@
#ifndef MATADOR_BASIC_REPOSITORY_HPP
#define MATADOR_BASIC_REPOSITORY_HPP
#include "matador/object/error_code.hpp"
#include "matador/object/object_info.hpp"
#include "matador/object/repository_node_iterator.hpp"
#include "matador/logger/logger.hpp"
#include "matador/utils/error.hpp"
#include "matador/utils/result.hpp"
#include <typeindex>
#include <unordered_map>
namespace matador::object {
utils::error make_error(error_code ec, const std::string &msg);
class repository_node;
class const_repository_node_iterator;
class basic_repository {
public:
typedef const_repository_node_iterator const_iterator; /**< Shortcut for the list const iterator. */
explicit basic_repository(std::string name = "");
~basic_repository();
/**
*
* @param node Node to attach
* @param parent Name of parent node
* @return Attached node
*/
[[nodiscard]] utils::result<repository_node*, utils::error> attach_node(repository_node *node, const std::string &parent);
/**
* Detaches a given node from the schema. If the
* node is a parent of other nodes, these nodes are
* detached as well.
*
* @param node Node to detach from schema
* @return Result object indicating success or failure
*/
[[nodiscard]] utils::result<void, utils::error> detach(repository_node *node);
/**
* Return the first schema node.
*
* @return The first schema node iterator.
*/
[[nodiscard]] const_iterator begin() const;
/**
* Return the last schema node.
*
* @return The last schema node iterator.
*/
[[nodiscard]] const_iterator end() const;
/**
* Returns true if the schema contains
* no schema nodes.
*
* @return True if the schema is empty
*/
[[nodiscard]] bool empty() const;
/**
* Returns the current number of the schema node.
*
* @return Number of schema nodes
*/
[[nodiscard]] size_t size() const;
/**
* Returns the name of the schema.
*
* @return The name of the schema
*/
[[nodiscard]] std::string name() const;
[[nodiscard]] bool contains(const std::string &name) const;
[[nodiscard]] bool contains(const std::type_index &index) const;
template < typename Type >
[[nodiscard]] bool contains() const {
return contains(std::type_index(typeid(Type)));
}
[[nodiscard]] utils::result<basic_object_info_ref, utils::error> basic_info(const std::type_index& ti) const;
[[nodiscard]] utils::result<basic_object_info_ref, utils::error> basic_info(const std::string &name) const;
template<typename Type>
[[nodiscard]] utils::result<basic_object_info_ref, utils::error> basic_info() const {
return basic_info(std::type_index(typeid(Type)));
}
[[nodiscard]] utils::result<const attribute*, utils::error> primary_key_attribute(const std::type_index &ti) const;
void dump(std::ostream &os) const;
static void dump(std::ostream &os, const repository_node& node);
protected:
using t_node_map = std::unordered_map<std::string, repository_node*>;
using t_type_index_node_map = std::unordered_map<std::type_index, repository_node*>;
protected:
[[nodiscard]] const_iterator find_node(const std::string &name) const;
[[nodiscard]] const_iterator find_node(const std::type_index &type_index) const;
template<typename Type>
[[nodiscard]] const_iterator find_node() const {
return find_node(std::type_index(typeid(Type)));
}
[[nodiscard]] bool has_node(const std::string &name) const;
[[nodiscard]] bool has_node(const std::type_index &index) const;
[[nodiscard]] bool has_node(const repository_node* node) const;
static void insert_node(repository_node *parent, repository_node *child);
void remove_node(repository_node *node);
[[nodiscard]] bool expecting_relation_node(const std::string &name) const;
void expect_relation_node(const std::string &name, const std::type_index &ti);
void remove_expected_relation_node(const std::string &name);
[[nodiscard]] std::shared_ptr<object> provide_object_in_advance(const std::type_index &ti, const std::shared_ptr<object>& obj);
[[nodiscard]] bool has_object_for_type(const std::type_index &ti) const;
[[nodiscard]] std::shared_ptr<object> object_for_type(const std::type_index &ti) const;
void remove_object_for_type(const std::type_index &ti);
[[nodiscard]] bool is_node_announced(const std::type_index &ti) const;
void push_announce_node(const std::type_index &ti, std::unique_ptr<repository_node> &&node);
[[nodiscard]] repository_node* announce_node(const std::type_index &ti) const;
[[nodiscard]] std::unique_ptr<repository_node> pop_announce_node(const std::type_index &ti);
protected:
friend class object_generator;
template < typename NodeType, template<typename> typename ...Observers >
friend class foreign_node_completer;
template < typename NodeType, template<typename> typename ...Observers >
friend class relation_completer;
std::string name_;
repository_node* root_{nullptr};
t_node_map nodes_by_name_;
t_type_index_node_map nodes_by_type_;
logger::logger log_;
std::unordered_map<std::type_index, std::unique_ptr<repository_node>> announced_node_;
std::unordered_map<std::type_index, attribute*> missing_references_{};
std::unordered_map<std::string, std::type_index> expected_relation_nodes_;
std::unordered_map<std::type_index, std::shared_ptr<object>> object_by_type_{};
};
}
#endif //MATADOR_BASIC_REPOSITORY_HPP
+45 -4
View File
@@ -1,18 +1,59 @@
#ifndef COLLECTION_HPP #ifndef COLLECTION_HPP
#define COLLECTION_HPP #define COLLECTION_HPP
#include <vector> #include "matador/object/collection_proxy.hpp"
namespace matador::object { namespace matador::object {
template < class Type > template < class Type >
class collection { class collection {
public: public:
using value_type = Type; using value_type = typename collection_proxy<Type>::value_type;
using iterator = typename collection_proxy<Type>::iterator;
using const_iterator = typename collection_proxy<Type>::const_iterator;
collection()
: proxy_(std::make_shared<collection_proxy<Type>>()) {}
explicit collection(std::shared_ptr<collection_proxy<Type>> proxy)
: proxy_(std::move(proxy)) {}
explicit collection(std::vector<Type> items)
: proxy_(std::make_shared<collection_proxy<Type>>(std::move(items))) {}
collection(const collection& other) = default;
void push_back(const value_type& value) {
proxy_->push_back(value);
}
iterator begin() {
return proxy_->begin();
}
iterator end() {
return proxy_->end();
}
const_iterator begin() const {
return proxy_->begin();
}
const_iterator end() const {
return proxy_->end();
}
[[nodiscard]] size_t size() const {
return proxy_->size();
}
[[nodiscard]] bool empty() const {
return proxy_->empty();
}
void reset(std::shared_ptr<collection_proxy<Type>> proxy) {
proxy_ = std::move(proxy);
}
private: private:
std::vector<Type> data_; std::shared_ptr<abstract_collection_proxy<Type>> proxy_;
}; };
} }
+188
View File
@@ -0,0 +1,188 @@
#ifndef MATADOR_COLLECTION_PROXY_HPP
#define MATADOR_COLLECTION_PROXY_HPP
#include "matador/object/collection_resolver.hpp"
#include "matador/object/many_to_many_relation.hpp"
#include "matador/utils/identifier.hpp"
#include <atomic>
#include <mutex>
#include <vector>
namespace matador::object {
// has many primitive
// relation<OwnerType, PrimitiveType>
// has many ptr
// relation<OwnerType, ObjectType>
// has many to many
// relation<OwnerType, ForeignType>
template < class RelationType >
struct relation_iterator_traits {
static RelationType& value(RelationType& item) {
return item;
}
};
template < typename Type, typename OwnerType >
struct relation_iterator_traits<relation<OwnerType, Type>> {
static Type& value(relation<OwnerType, Type>& re) {
return re.relation().value();
}
};
template<typename Type, typename RelationType = Type>
class collection_proxy_iterator {
public:
using value_type = Type;
using reference = Type&;
using pointer = Type*;
using relation_type = RelationType;
using iterator_category = std::forward_iterator_tag;
collection_proxy_iterator() = default;
collection_proxy_iterator(typename std::vector<RelationType>::iterator it)
: it_(it) {}
reference operator*() {
return relation_iterator_traits<relation_type>::value(*it_);
}
pointer operator->() {
return &relation_iterator_traits<relation_type>::value(*it_);
}
collection_proxy_iterator& operator++() {
++it_;
return *this;
}
bool operator==(const collection_proxy_iterator& other) const {
return it_ == other.it_;
}
bool operator!=(const collection_proxy_iterator& other) const {
return !operator==(other);
}
// relation_type& relation() {
// return *it_;
// }
private:
typename std::vector<RelationType>::iterator it_;
};
template<typename Type, typename RelationType = Type>
class abstract_collection_proxy {
public:
using value_type = Type;
using relation_type = RelationType;
// using iterator = typename std::vector<value_type>::iterator;
// using const_iterator = typename std::vector<value_type>::const_iterator;
using iterator = collection_proxy_iterator<value_type, relation_type>;
using const_iterator = collection_proxy_iterator<value_type, relation_type>;
virtual ~abstract_collection_proxy() = default;
virtual void push_back(const value_type& value) = 0;
virtual iterator begin() = 0;
virtual iterator end() = 0;
[[nodiscard]] virtual size_t size() = 0;
[[nodiscard]] virtual bool empty() = 0;
[[nodiscard]] virtual const utils::identifier& owner_id() const = 0;
protected:
std::vector<relation_type> items_;
};
template<typename Type>
class collection_proxy : public abstract_collection_proxy<Type> {
public:
using value_type = Type;
using iterator = typename abstract_collection_proxy<Type>::iterator;
using const_iterator = typename abstract_collection_proxy<Type>::const_iterator;
collection_proxy() = default;
// Lazy
collection_proxy(std::weak_ptr<collection_resolver<Type>> resolver, utils::identifier owner_id)
: owner_id_(std::move(owner_id)), resolver_(std::move(resolver)) {}
// Eager
collection_proxy(std::weak_ptr<collection_resolver<Type>> resolver, std::vector<Type> items)
: loaded_{true}, items_(std::move(items)), resolver_(std::move(resolver)) {}
// Transient
explicit collection_proxy(std::vector<Type> items)
: items_(std::move(items)) {}
[[nodiscard]] const utils::identifier& owner_id() const override {
return owner_id_;
}
iterator begin() override {
resolve();
return items_.begin();
}
iterator end() override {
resolve();
return items_.end();
}
void push_back(const value_type& value) override {
resolve();
items_.push_back(value);
}
[[nodiscard]] size_t size() override {
resolve();
return items_.size();
}
[[nodiscard]] bool empty() override {
resolve();
return items_.empty();
}
private:
void resolve() {
if (loaded_) {
return;
}
std::lock_guard lock(mutex_);
auto resolver = resolver_.lock();
if (!resolver) {
return;
// Todo: Add states (Detached, Attached, Transient) - if attached an no resolver is available throw runtime exception
// throw std::runtime_error("Detached proxy (session expired)");
}
items_ = resolver->resolve(owner_id_);
loaded_ = true;
}
private:
const utils::identifier owner_id_;
std::atomic_bool loaded_{false};
std::vector<Type> items_;
std::weak_ptr<collection_resolver<Type>> resolver_{};
mutable std::mutex mutex_{};
};
template<typename Type, class OwnerType>
class collection_many_to_many_proxy {
public:
using value_type = Type;
using owner_type = OwnerType;
using relation_type = many_to_many_relation<value_type, owner_type>;
using iterator = typename std::vector<relation_type>::iterator;
using const_iterator = typename std::vector<relation_type>::const_iterator;
private:
std::vector<relation_type> relations_;
};
}
#endif //MATADOR_COLLECTION_PROXY_HPP
@@ -0,0 +1,23 @@
#ifndef MATADOR_COLLECTION_RESOLVER_HPP
#define MATADOR_COLLECTION_RESOLVER_HPP
#include "matador/object/abstract_joined_resolver.hpp"
#include <typeindex>
#include <vector>
namespace matador::utils {
class identifier;
}
namespace matador::object {
template<typename Type>
class collection_resolver : public abstract_joined_resolver {
public:
collection_resolver(const std::type_index& root_type, std::string collection_name)
: abstract_joined_resolver(root_type, typeid(Type), std::move(collection_name)) {}
virtual std::vector<Type> resolve(const utils::identifier& id) = 0;
};
}
#endif //MATADOR_COLLECTION_RESOLVER_HPP
@@ -0,0 +1,20 @@
#ifndef MATADOR_COLLECTION_UTILS_HPP
#define MATADOR_COLLECTION_UTILS_HPP
#include <string>
#include <typeindex>
namespace matador::object {
struct collection_composite_key {
std::type_index root_type;
std::type_index type;
std::string name;
bool operator==(const collection_composite_key& other) const;
};
struct collection_composite_key_hash {
std::size_t operator()(const collection_composite_key& k) const noexcept;
};
}
#endif //MATADOR_COLLECTION_UTILS_HPP
+1
View File
@@ -10,6 +10,7 @@ enum class error_code : uint8_t {
OK = 0, OK = 0,
NodeNotFound = 1, NodeNotFound = 1,
NodeAlreadyExists = 2, NodeAlreadyExists = 2,
NoPrimaryKey = 3,
Failure, Failure,
}; };
+114 -66
View File
@@ -1,8 +1,9 @@
#ifndef FOREIGN_NODE_COMPLETER_HPP #ifndef FOREIGN_NODE_COMPLETER_HPP
#define FOREIGN_NODE_COMPLETER_HPP #define FOREIGN_NODE_COMPLETER_HPP
#include "matador/object/internal/shadow_schema.hpp" #include "matador/object/basic_repository.hpp"
#include "matador/object/schema_node.hpp" #include "matador/object/internal/observer_list_copy_creator.hpp"
#include "matador/object/repository_node.hpp"
#include "matador/object/join_columns_collector.hpp" #include "matador/object/join_columns_collector.hpp"
#include "matador/object/object_ptr.hpp" #include "matador/object/object_ptr.hpp"
@@ -11,106 +12,153 @@
#include "matador/logger/log_manager.hpp" #include "matador/logger/log_manager.hpp"
#include <stack> #include <stack>
#include <vector>
namespace matador::object { namespace matador::object {
template<typename NodeType, template<typename> typename... Observers>
class completer {
public:
template<typename Type>
void foo(Observers<NodeType>&&... observers) {
(std::unique_ptr<Observers<Type>>(new Observers(std::forward<Observers>(observers))), ...);
}
};
/** /**
* Processes the given node and ensures * Processes the given node and ensures
* that all foreign nodes needed by the given node * that all foreign nodes needed by the given node
* relations are attached in schema. * relations are attached in schema.
*
* @tparam Type Type of node to complete
*/ */
template<typename NodeType, template<typename> typename ...Observers>
class foreign_node_completer final { class foreign_node_completer final {
private:
using node_ptr = std::shared_ptr<schema_node>;
public: public:
template<class Type> static void complete(repository_node *node, const std::vector<std::unique_ptr<observer<NodeType>>> &observers) {
static void complete(const std::shared_ptr<schema_node> &node) { foreign_node_completer completer(node->repo_, observers);
internal::shadow_schema shadow(node->schema_);
foreign_node_completer completer(shadow);
completer.complete_node<Type>(node); completer.complete_node(node);
} }
template<class PrimaryKeyType> template<typename BaseType>
static void on_primary_key(const char * /*id*/, PrimaryKeyType &/*pk*/, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {} static void on_base(const BaseType&) {}
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {} template<class PrimaryKeyType>
template<typename AttributeType> static void on_primary_key(const char * /*id*/, PrimaryKeyType &/*pk*/, const utils::primary_key_attribute & /*attr*/) {}
static void on_attribute(const char * /*id*/, AttributeType &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
template<typename AttributeType> template<typename AttributeType>
static void on_attribute(const char * /*id*/, std::optional<AttributeType> &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} static void on_attribute(const char * /*id*/, AttributeType &/*val*/, const utils::field_attributes &/*attr*/) {}
template<typename AttributeType>
static void on_attribute(const char * /*id*/, std::optional<AttributeType> &/*val*/, const utils::field_attributes &/*attr*/) {}
template<class ForeignPointerType>
void on_belongs_to(const char *id, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/);
template<class ForeignPointerType>
void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const char *join_column, const utils::foreign_attributes &/*attr*/);
template<class CollectionType>
void on_has_many(const char *id, CollectionType &,
const char *join_column,
const utils::foreign_attributes &attr,
std::enable_if_t<is_object_ptr<typename CollectionType::value_type>::value> * = nullptr);
template<class ForeignPointerType> template<class CollectionType>
void on_belongs_to(const char *id, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/); void on_has_many(const char * /*id*/,
template<class ForeignPointerType> CollectionType &,
void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/); const char * /*join_column*/,
const utils::foreign_attributes & /*attr*/,
std::enable_if_t<!is_object_ptr<typename CollectionType::value_type>::value> * = nullptr) {
}
template<class CollectionType> template<class CollectionType>
void on_has_many(const char *id, CollectionType &, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t<is_object_ptr<typename CollectionType::value_type>::value> * = nullptr); static void on_has_many_to_many(const char * /*id*/,
template<class CollectionType> CollectionType &/*collection*/,
void on_has_many(const char * /*id*/, CollectionType &, const char * /*join_column*/, const utils::foreign_attributes & /*attr*/, std::enable_if_t<!is_object_ptr<typename CollectionType::value_type>::value> * = nullptr) {} const char * /*join_column*/,
const char * /*inverse_join_column*/,
template<class CollectionType> const utils::foreign_attributes &/*attr*/) {}
void on_has_many_to_many(const char *id, CollectionType &collection, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &attr); template<class CollectionType>
template<class CollectionType> static void on_has_many_to_many(const char * /*id*/, CollectionType &/*collection*/, const utils::foreign_attributes &/*attr*/) {}
void on_has_many_to_many(const char *id, CollectionType &collection, const utils::foreign_attributes &attr);
private: private:
explicit foreign_node_completer(internal::shadow_schema &shadow); explicit foreign_node_completer(basic_repository &repo, const std::vector<std::unique_ptr<observer<NodeType>>> &observers)
: repo_(repo)
, log_(logger::create_logger("node_completer"))
, observers_(observers) {}
template<typename Type> void complete_node(repository_node *node) {
void complete_node(const std::shared_ptr<schema_node> &node) { nodes_.push(node);
nodes_.push(node);
Type obj; NodeType obj;
access::process(*this, obj); access::process(*this, obj);
nodes_.pop(); nodes_.pop();
}
template<typename Type>
void attach_node() {
if (repo_.contains(typeid(Type))) {
return;
} }
template<typename Type> auto observers = internal::observer_list_copy_creator<NodeType, Type, Observers...>::copy_create(observers_);
void attach_node() {
if (schema_.schema_contains(typeid(Type))) { if (repo_.is_node_announced(typeid(Type))) {
return; return;
}
const auto node = schema_node::make_node<Type>(schema_.schema(), "");
if (auto result = schema_.attach_node(node)) {
complete<Type>(result.value());
}
} }
auto node = repository_node::make_node<Type>(repo_, "", []{ return std::make_unique<Type>(); }, std::move(observers));
repo_.push_announce_node(typeid(Type), std::move(node));
// if (auto result = repo_.attach_node(node)) {
// foreign_node_completer<Type>::complete(result.value(), {});
// }
}
private: private:
std::stack<node_ptr> nodes_; using endpoint_ptr = std::shared_ptr<relation_endpoint>;
internal::shadow_schema &schema_; static void register_relation_endpoints(const endpoint_ptr &endpoint,
logger::logger log_; const endpoint_ptr &other_endpoint);
join_columns_collector join_columns_collector_{}; static void link_relation_endpoints(const endpoint_ptr &endpoint,
const endpoint_ptr &other_endpoint);
private:
template<typename Type, template<typename> typename ...OtherObservers>
friend class foreign_node_completer;
private:
std::stack<repository_node*> nodes_;
basic_repository &repo_;
logger::logger log_;
join_columns_collector join_columns_collector_{};
const std::vector<std::unique_ptr<observer<NodeType>>>& observers_;
}; };
template<typename NodeType, template<typename> typename ...Observers>
template<class ForeignPointerType> template<class ForeignPointerType>
void foreign_node_completer::on_belongs_to( const char* /*id*/, ForeignPointerType&, const utils::foreign_attributes& ) { void foreign_node_completer<NodeType, Observers...>::on_belongs_to(const char * /*id*/, ForeignPointerType &, const utils::foreign_attributes &) {
attach_node<typename ForeignPointerType::value_type>(); attach_node<typename ForeignPointerType::value_type>();
} }
template<typename NodeType, template<typename> typename ...Observers>
template<class ForeignPointerType> template<class ForeignPointerType>
void foreign_node_completer::on_has_one( const char*, ForeignPointerType&, const utils::foreign_attributes& ) { void foreign_node_completer<NodeType, Observers...>::on_has_one(const char * /*id*/, ForeignPointerType &, const char * /*join_column*/, const utils::foreign_attributes &) {
attach_node<typename ForeignPointerType::value_type>(); attach_node<typename ForeignPointerType::value_type>();
} }
template<typename NodeType, template<typename> typename ...Observers>
template<class CollectionType> template<class CollectionType>
void foreign_node_completer::on_has_many( const char* /*id*/, CollectionType&, const char* /*join_column*/, const utils::foreign_attributes& /*attr*/, std::enable_if_t<is_object_ptr<typename CollectionType::value_type>::value>* ) { void foreign_node_completer<NodeType, Observers...>::on_has_many(const char * /*id*/,
attach_node<typename CollectionType::value_type::value_type>(); CollectionType &, const char * /*join_column*/,
const utils::foreign_attributes & /*attr*/,
std::enable_if_t<is_object_ptr<typename CollectionType::value_type>::value> *) {
attach_node<typename CollectionType::value_type::value_type>();
} }
template<class CollectionType> template<typename NodeType, template<typename> typename ...Observers>
void foreign_node_completer::on_has_many_to_many( const char* /*id*/, CollectionType& /*collection*/, const char* /*join_column*/, const char* /*inverse_join_column*/, const utils::foreign_attributes& /*attr*/ ) { void foreign_node_completer<NodeType, Observers...>::register_relation_endpoints(const endpoint_ptr &endpoint,
attach_node<typename CollectionType::value_type::value_type>(); const endpoint_ptr &other_endpoint) {
endpoint->node().info_->register_relation_endpoint(other_endpoint->node().type_index(), endpoint);
other_endpoint->node().info_->register_relation_endpoint(endpoint->node().type_index(), other_endpoint);
link_relation_endpoints(endpoint, other_endpoint);
} }
template<class CollectionType> template<typename NodeType, template<typename> typename ...Observers>
void foreign_node_completer::on_has_many_to_many( const char* /*id*/, CollectionType& /*collection*/, const utils::foreign_attributes& /*attr*/ ) { void foreign_node_completer<NodeType, Observers...>::link_relation_endpoints(const endpoint_ptr &endpoint, const endpoint_ptr &other_endpoint) {
attach_node<typename CollectionType::value_type::value_type>(); endpoint->link_foreign_endpoint(other_endpoint);
other_endpoint->link_foreign_endpoint(endpoint);
} }
} }
#endif //FOREIGN_NODE_COMPLETER_HPP #endif //FOREIGN_NODE_COMPLETER_HPP
@@ -0,0 +1,57 @@
#ifndef MATADOR_OBSERVER_LIST_COPY_CREATOR_HPP
#define MATADOR_OBSERVER_LIST_COPY_CREATOR_HPP
#include "matador/object/observer.hpp"
#include <memory>
#include <vector>
namespace matador::object::internal {
template <typename SourceType, typename DestType, template <typename> class... ObserverType>
class observer_list_copy_creator
{
public:
using source_observer_vector = std::vector<std::unique_ptr<observer<SourceType>>>;
using observer_vector = std::vector<std::unique_ptr<observer<DestType>>>;
using iterator = typename observer_vector::iterator;
static observer_vector copy_create(const source_observer_vector &source_observers) {
observer_list_copy_creator creator(source_observers);
return std::move(creator.observers_);
}
private:
explicit observer_list_copy_creator(const source_observer_vector &source_observers)
: source_observers_(source_observers) {
if constexpr (sizeof...(ObserverType) != 0) {
copy_observer<ObserverType...>();
}
}
template <template <typename> class FirstObserverType>
void copy_observer() {
try_copy_observer<FirstObserverType>();
}
template <template <typename> class FirstObserverType, template <typename> class NextObserverType, template <typename> class... RestObserverType>
void copy_observer() {
try_copy_observer<FirstObserverType>();
copy_observer<NextObserverType, RestObserverType...>();
}
template <template <typename> class CurrentObserverType>
void try_copy_observer() {
for ( const auto &obs : source_observers_ ) {;
if (const auto *casted_observer = dynamic_cast<const CurrentObserverType<SourceType>*>(obs.get()); casted_observer != nullptr) {
observers_.emplace_back(std::make_unique<CurrentObserverType<DestType>>(*casted_observer));
break;
}
}
}
private:
const source_observer_vector &source_observers_;
observer_vector observers_;
};
}
#endif //MATADOR_OBSERVER_LIST_COPY_CREATOR_HPP
@@ -0,0 +1,69 @@
#ifndef MATADOR_OBSERVER_LIST_CREATOR_HPP
#define MATADOR_OBSERVER_LIST_CREATOR_HPP
#include "matador/object/observer.hpp"
#include <memory>
#include <vector>
namespace matador::object::internal {
template <typename Type, template <typename> class... ObserverType>
class observer_list_creator
{
public:
using observer_vector = std::vector<std::unique_ptr<observer<Type>>>;
static void create_missing(observer_vector &observers) {
observer_list_creator creator(observers);
}
private:
explicit observer_list_creator(observer_vector &observers)
: observers_(observers) {
if constexpr (sizeof...(ObserverType) != 0) {
build_observer<ObserverType...>();
}
}
template <template <typename> class FirstObserverType>
void build_observer() {
try_copy_on_missing<FirstObserverType>();
}
template <template <typename> class FirstObserverType, template <typename> class NextObserverType, template <typename> class... RestObserverType>
void build_observer() {
try_copy_on_missing<FirstObserverType>();
build_observer<NextObserverType, RestObserverType...>();
}
template <template <typename> class CurrentObserverType>
void try_copy_on_missing() {
bool is_missing{true};
for ( const auto &obs : observers_ ) {;
if (dynamic_cast<const CurrentObserverType<Type>*>(obs.get())) {
is_missing = false;
break;
}
}
if (is_missing) {
append<CurrentObserverType<Type>>();
}
}
template <class CurrentObserverType>
void append(std::enable_if_t<std::is_default_constructible_v<CurrentObserverType> || std::is_trivially_default_constructible_v<CurrentObserverType>>* = nullptr) {
observers_.emplace_back(std::make_unique<CurrentObserverType>());
}
template <class CurrentObserverType>
void append(std::enable_if_t<!std::is_default_constructible_v<CurrentObserverType> && !std::is_trivially_default_constructible_v<CurrentObserverType>>* = nullptr) {
static_assert("no default constructor");
}
private:
observer_vector &observers_;
};
}
#endif //MATADOR_OBSERVER_LIST_CREATOR_HPP
@@ -1,34 +0,0 @@
#ifndef SHADOW_SCHEMA_HPP
#define SHADOW_SCHEMA_HPP
#include "matador/utils/result.hpp"
#include "matador/utils/error.hpp"
#include <memory>
#include <typeindex>
namespace matador::object {
class schema;
class schema_node;
}
namespace matador::object::internal {
class shadow_schema {
private:
using node_ptr = std::shared_ptr<schema_node>;
public:
explicit shadow_schema(object::schema& s);
[[nodiscard]] object::schema& schema() const;
[[nodiscard]] bool schema_contains(const std::type_index& ti) const;
[[nodiscard]] utils::result<node_ptr, utils::error> find_node(const std::type_index &type_index) const;
[[nodiscard]] utils::result<node_ptr, utils::error> find_node(const std::string &name) const;
[[nodiscard]] utils::result<node_ptr, utils::error> attach_node(const std::shared_ptr<schema_node> &node) const;
[[nodiscard]] utils::result<void, utils::error> detach_node(const std::shared_ptr<schema_node> &node) const;
private:
object::schema& schema_;
};
}
#endif //SHADOW_SCHEMA_HPP
@@ -24,17 +24,19 @@ public:
return join_columns_; return join_columns_;
} }
template<typename BaseType>
static void on_base(const BaseType&) {}
template < class V > template < class V >
static void on_primary_key(const char * /*id*/, V &, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {} static void on_primary_key(const char * /*id*/, V &, const utils::primary_key_attribute& /*attr*/) {}
static void on_revision(const char * /*id*/, unsigned long long &/*rev*/) {} static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
template<typename Type> template<typename Type>
static void on_attribute(const char * /*id*/, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} static void on_attribute(const char * /*id*/, Type &, const utils::field_attributes &/*attr*/) {}
template<class Pointer> template<class Pointer>
static void on_belongs_to(const char * /*id*/, Pointer &obj, const utils::foreign_attributes &attr) {} static void on_belongs_to(const char * /*id*/, Pointer &/*obj*/, const utils::foreign_attributes &/*attr*/) {}
template<class Pointer> template<class Pointer>
static void on_has_one(const char * /*id*/, Pointer &obj, const utils::foreign_attributes &attr) {} static void on_has_one(const char * /*id*/, Pointer &/*obj*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
static void on_has_many(ContainerType &, const char *join_column, const utils::foreign_attributes &attr) {} static void on_has_many(ContainerType &, const char */*join_column*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
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*/) { 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_.join_column = join_column;
@@ -0,0 +1,51 @@
#ifndef MATADOR_CONTAINER_RESOLVER_FACTORY_HPP
#define MATADOR_CONTAINER_RESOLVER_FACTORY_HPP
#include "matador/object/collection_resolver.hpp"
#include <memory>
namespace matador::object {
class abstract_collection_resolver_factory {
public:
virtual ~abstract_collection_resolver_factory() = default;
[[nodiscard]] virtual std::shared_ptr<abstract_joined_resolver> acquire_collection_resolver(const std::type_index &root_type, const std::type_index &element_type, const std::string &collection_name) const = 0;
virtual void register_collection_resolver(std::shared_ptr<abstract_joined_resolver> &&resolver) = 0;
};
class joined_collection_resolver_factory : public abstract_collection_resolver_factory {
public:
template<class Type>
[[nodiscard]] std::shared_ptr<collection_resolver<Type>> resolver(const std::type_index &root_type, const std::string &collection_name) const {
const auto res = acquire_collection_resolver(root_type, typeid(Type), collection_name);
if (!res) {
return std::dynamic_pointer_cast<collection_resolver<Type>>(res);
}
return std::dynamic_pointer_cast<collection_resolver<Type>>(res);
}
};
class abstract_joined_object_resolver_factory {
public:
virtual ~abstract_joined_object_resolver_factory() = default;
[[nodiscard]] virtual std::shared_ptr<abstract_type_resolver> acquire_joined_object_resolver(const std::type_index &root_type, const std::type_index &element_type, const std::string &collection_name) const = 0;
virtual void register_joined_object_resolver(std::shared_ptr<abstract_type_resolver> &&resolver, const std::type_index& root_type, const std::string& join_column) = 0;
};
class joined_object_resolver_factory : public abstract_joined_object_resolver_factory {
public:
template<class Type>
[[nodiscard]] std::shared_ptr<object_resolver<Type>> resolver(const std::type_index &root_type, const std::string &collection_name) const {
const auto res = acquire_joined_object_resolver(root_type, typeid(Type), collection_name);
if (!res) {
return std::dynamic_pointer_cast<object_resolver<Type>>(res);
}
return std::dynamic_pointer_cast<object_resolver<Type>>(res);
}
};
}
#endif //MATADOR_CONTAINER_RESOLVER_FACTORY_HPP
@@ -8,6 +8,48 @@
namespace matador::object { namespace matador::object {
template < class LocalType, class ForeignType >
class relation {
public:
relation() = default;
relation(std::string local_name, std::string remote_name)
: local_name_(std::move(local_name))
, remote_name_(std::move(remote_name)) {}
relation(std::string local_name, std::string remote_name, const object_ptr<LocalType>& local, const ForeignType& remote)
: local_name_(std::move(local_name))
, remote_name_(std::move(remote_name))
, local_(local)
, remote_(remote)
{}
template<class Operator>
void process(Operator &op) {
namespace field = matador::access;
field::belongs_to(op, local_name_.c_str(), local_, utils::CascadeNoneFetchLazy);
foreign_field(op);
}
object_ptr<LocalType> local() const { return local_; }
const ForeignType& remote() const { return remote_; }
private:
template<typename Operator, class RemoteType = ForeignType>
void foreign_field(Operator &op, std::enable_if_t<!is_object_ptr<RemoteType>::value>* /*unused*/) {
namespace field = matador::access;
field::attribute(op, remote_name_.c_str(), remote_);
}
template<typename Operator, class RemoteType = ForeignType>
void foreign_field(Operator &op, std::enable_if_t<is_object_ptr<RemoteType>::value>* /*unused*/) {
namespace field = matador::access;
field::belongs_to(op, remote_name_.c_str(), remote_, utils::CascadeNoneFetchLazy);
}
private:
std::string local_name_;
std::string remote_name_;
object_ptr<LocalType> local_;
ForeignType remote_;
};
template < class LocalType, class ForeignType > template < class LocalType, class ForeignType >
class many_to_many_relation { class many_to_many_relation {
public: public:
@@ -15,12 +57,18 @@ public:
many_to_many_relation(std::string local_name, std::string remote_name) many_to_many_relation(std::string local_name, std::string remote_name)
: local_name_(std::move(local_name)) : local_name_(std::move(local_name))
, remote_name_(std::move(remote_name)) {} , remote_name_(std::move(remote_name)) {}
many_to_many_relation(std::string local_name, std::string remote_name, const object_ptr<LocalType>& local, const object_ptr<ForeignType>& remote)
: local_name_(std::move(local_name))
, remote_name_(std::move(remote_name))
, local_(local)
, remote_(remote)
{}
template<class Operator> template<class Operator>
void process(Operator &op) { void process(Operator &op) {
namespace field = matador::access; namespace field = matador::access;
field::belongs_to(op, local_name_.c_str(), local_, utils::default_foreign_attributes); field::belongs_to(op, local_name_.c_str(), local_, utils::CascadeNoneFetchLazy);
field::belongs_to(op, remote_name_.c_str(), remote_, utils::default_foreign_attributes); field::belongs_to(op, remote_name_.c_str(), remote_, utils::CascadeNoneFetchLazy);
} }
object_ptr<LocalType> local() const { return local_; } object_ptr<LocalType> local() const { return local_; }
@@ -40,11 +88,16 @@ public:
many_to_relation(std::string local_name, std::string remote_name) many_to_relation(std::string local_name, std::string remote_name)
: local_name_(std::move(local_name)) : local_name_(std::move(local_name))
, type_name_(std::move(remote_name)) {} , type_name_(std::move(remote_name)) {}
many_to_relation(std::string local_name, std::string remote_name, const object_ptr<LocalType>& local, const Type& value)
: local_name_(std::move(local_name))
, type_name_(std::move(remote_name))
, local_(local)
, value_(value){}
template<class Operator> template<class Operator>
void process(Operator &op) { void process(Operator &op) {
namespace field = matador::access; namespace field = matador::access;
field::belongs_to(op, local_name_.c_str(), local_, utils::default_foreign_attributes); field::belongs_to(op, local_name_.c_str(), local_, utils::CascadeNoneFetchLazy);
field::attribute(op, type_name_.c_str(), value_); field::attribute(op, type_name_.c_str(), value_);
} }
+56
View File
@@ -0,0 +1,56 @@
#ifndef MATADOR_OBJECT_HPP
#define MATADOR_OBJECT_HPP
#include "matador/object/attribute.hpp"
#include "matador/object/restriction.hpp"
#include "matador/utils/identifier.hpp"
#include <list>
#include <ostream>
namespace matador::object {
class object {
public:
explicit object(std::string name);
static const attribute& create_attribute(std::string name, const std::shared_ptr<object>& obj);
[[nodiscard]] const attribute* primary_key_attribute() const;
[[nodiscard]] const utils::identifier& primary_key() const;
[[nodiscard]] bool has_primary_key() const;
[[nodiscard]] bool is_relation_object() const;
[[nodiscard]] const attribute* join_attribute() const;
[[nodiscard]] const attribute* inverse_join_attribute() const;
[[nodiscard]] const std::string& name() const;
void update_name(const std::string& name);
[[nodiscard]] bool has_attributes() const;
[[nodiscard]] size_t attribute_count() const;
[[nodiscard]] const std::vector<attribute>& attributes() const;
[[nodiscard]] bool has_constraints() const;
[[nodiscard]] size_t constraint_count() const;
[[nodiscard]] const std::list<restriction>& constraints() const;
friend std::ostream& operator<<(std::ostream& os, const object& obj);
private:
friend class attribute_generator;
friend class object_generator;
std::string name_;
int pk_column_index_{-1};
int join_column_index_{-1};
int inverse_join_column_index_{-1};
utils::identifier pk_identifier_;
std::vector<attribute> attributes_;
std::list<restriction> constraints_;
};
}
#endif //MATADOR_OBJECT_HPP
+429
View File
@@ -0,0 +1,429 @@
#ifndef MATADOR_OBJECT_CACHE_HPP
#define MATADOR_OBJECT_CACHE_HPP
#include "matador/object/object_proxy.hpp"
#include "matador/object/object_resolver.hpp"
#include "matador/utils/identifier.hpp"
#include "matador/utils/message_bus.hpp"
#include <unordered_map>
#include <memory>
#include <shared_mutex>
#include <typeindex>
#include <utility>
#include <cassert>
namespace matador::object {
struct cache_entry_base {
virtual ~cache_entry_base() = default;
/**
* @brief Gibt an, ob der Eintrag vollständig "tot" ist (weder Proxy noch Entity leben).
*
* Wird von @ref object_cache::sweep() verwendet, um Einträge ohne T-Kenntnis zu bereinigen.
*/
[[nodiscard]] virtual bool is_dead() const noexcept = 0;
};
template<typename Type>
struct cache_entry : cache_entry_base {
std::weak_ptr<object_proxy<Type> > proxy;
std::weak_ptr<Type> entity;
[[nodiscard]] bool is_dead() const noexcept override {
return proxy.expired() && entity.expired();
}
};
struct object_cache_event {
std::type_index type{typeid(void)};
utils::identifier id{};
std::chrono::steady_clock::time_point timestamp{};
};
struct object_cache_accessed_event : object_cache_event {};
struct object_cache_proxy_added_event : object_cache_event {};
struct object_cache_entity_attached_event : object_cache_event {};
struct object_cache_imported_event : object_cache_event {};
struct object_cache_erased_event : object_cache_event {};
struct object_cache_sweep_event {
std::size_t removed{};
std::chrono::steady_clock::time_point timestamp{};
};
/**
* @brief Thread-sicherer Cache für Objekt-Proxies und (optional) geladene Entities.
*
* Der Cache verwaltet Einträge pro Kombination aus Objekt-Typ @p T und Primärschlüssel/Identifier.
* Für jede (T, id)-Kombination können sowohl
* - ein Proxy (@c object_proxy<T>) als auch
* - die dazugehörige Entity (@c T)
* zwischengespeichert werden.
*
* Der Cache hält dabei nur @c std::weak_ptr auf Proxy und Entity, d.h. er verlängert deren Lebensdauer nicht.
*
* @note Thread-Safety: Alle öffentlichen Methoden sind threadsafe (intern synchronisiert).
* @note Semantik: Ein Eintrag kann aus dem Cache opportunistisch entfernt werden, sobald Proxy und Entity abgelaufen sind.
*/
class object_cache {
public:
/**
* @brief Erzeugt einen leeren Cache.
*/
explicit object_cache(utils::message_bus &bus)
: bus_(bus) {}
/**
* @brief Liefert einen Proxy für (T, id) und erstellt ihn bei Bedarf.
*
* Falls bereits ein lebender Proxy im Cache existiert, wird er wiederverwendet.
* Andernfalls wird ein neuer Proxy erzeugt, im Cache abgelegt und zurückgegeben.
* Falls für (T, id) bereits eine Entity vorhanden ist, wird sie an den Proxy gebunden.
*
* @tparam Type Entity-Typ.
* @tparam ResolverPointerType Zeiger-Typ auf einen Resolver, typischerweise
* @c std::shared_ptr<object_resolver<T>> oder @c std::weak_ptr<object_resolver<T>>.
* @param id Identifier/Primärschlüssel der Entity.
* @param resolver_ptr Resolver (shared/weak), der zur Lazy-Auflösung durch den Proxy genutzt wird.
* @return Shared-Pointer auf den (ggf. neu erstellten) Proxy.
*
* @note Diese Methode ist threadsafe.
*/
template<typename Type, typename ResolverPointerType>
std::shared_ptr<object_proxy<Type>> acquire_proxy(utils::identifier id, ResolverPointerType &&resolver_ptr) {
const auto k = make_key<Type>(id);
std::unique_lock lock(mutex_);
auto it = map_.find(k);
if (it != map_.end()) {
// found entry, return std::shared_ptr of proxy
auto *entry = entry_cast_<Type>(it->second.get());
if (auto proxy_ptr = entry->proxy.lock()) {
bus_.publish<object_cache_accessed_event>({k.type, id, std::chrono::steady_clock::now()});
return proxy_ptr;
}
// if the proxy is dead, but the entity is alive, create a new proxy and attach entity
auto weak_resolver = to_weak<Type>(std::forward<ResolverPointerType>(resolver_ptr));
auto proxy_ptr = std::make_shared<object_proxy<Type>>(weak_resolver, id);
if (auto obj = entry->entity.lock()) {
proxy_ptr->attach(std::move(obj));
}
entry->proxy = proxy_ptr;
bus_.publish<object_cache_proxy_added_event>({k.type, id, std::chrono::steady_clock::now()});
// return the shared_ptr of the proxy
return proxy_ptr;
}
// entry couldn't be found, create a new entry
auto entry_ptr = std::make_unique<cache_entry<Type>>();
auto *entry = entry_ptr.get();
// create a weak resolver and shared proxy
auto weak_resolver = to_weak<Type>(std::forward<ResolverPointerType>(resolver_ptr));
auto proxy_ptr = std::make_shared<object_proxy<Type>>(weak_resolver, id);
// lock entity and attach to proxy
if (auto obj = entry->entity.lock()) {
proxy_ptr->attach(std::move(obj));
}
// set the new proxy into cache entry
entry->proxy = proxy_ptr;
map_.emplace(k, std::move(entry_ptr));
bus_.publish<object_cache_proxy_added_event>({k.type, id, std::chrono::steady_clock::now()});
// return the shared_ptr of the proxy
return proxy_ptr;
}
template<typename Type, typename ResolverPointerType>
bool import(const utils::identifier &id, const std::shared_ptr<object_proxy<Type>> &proxy, ResolverPointerType &&resolver_ptr) {
if (!proxy) {
return false;
}
auto obj = proxy->object();
auto weak_resolver = to_weak<Type>(std::forward<ResolverPointerType>(resolver_ptr));
const auto k = make_key<Type>(id);
std::unique_lock lock(mutex_);
auto it = map_.find(k);
if (it != map_.end()) {
auto *entry = entry_cast_<Type>(it->second.get());
if (auto existing_proxy = entry->proxy.lock()) {
if (existing_proxy != proxy) {
return false;
}
}
proxy->primary_key(id);
proxy->resolver(std::move(weak_resolver));
if (obj) {
entry->entity = obj;
proxy->attach(std::move(obj));
}
entry->proxy = proxy;
bus_.publish<object_cache_imported_event>({k.type, id, std::chrono::steady_clock::now()});
return true;
}
auto entry_ptr = std::make_unique<cache_entry<Type>>();
auto *entry = entry_ptr.get();
proxy->primary_key(id);
proxy->resolver(std::move(weak_resolver));
if (obj) {
entry->entity = obj;
proxy->attach(std::move(obj));
}
entry->proxy = proxy;
map_.emplace(k, std::move(entry_ptr));
bus_.publish<object_cache_imported_event>({k.type, id, std::chrono::steady_clock::now()});
return true;
}
/**
* @brief Verknüpft eine geladene Entity mit einem Cache-Eintrag (Type, id).
*
* Speichert die Entity als @c weak_ptr im Cache. Falls bereits ein Proxy existiert,
* wird die Entity sofort an den Proxy gebunden.
*
* @tparam Type Entity-Typ.
* @param id Identifier/Primärschlüssel der Entity.
* @param obj Geladene Entity als @c shared_ptr.
*
* @note Diese Methode ist threadsafe.
*/
template<typename Type>
void attach_entity(const utils::identifier &id, std::shared_ptr<Type> obj) {
const auto k = make_key<Type>(id);
std::unique_lock lock(mutex_);
auto it = map_.find(k);
if (it == map_.end()) {
auto entry_ptr = std::make_unique<cache_entry<Type>>();
auto *entry = entry_ptr.get();
entry->entity = obj;
map_.emplace(k, std::move(entry_ptr));
bus_.publish<object_cache_entity_attached_event>({k.type, id, std::chrono::steady_clock::now()});
return;
}
auto *entry = entry_cast_<Type>(it->second.get());
entry->entity = obj;
if (auto proxy = entry->proxy.lock()) {
proxy->attach(std::move(obj));
}
bus_.publish<object_cache_entity_attached_event>({k.type, id, std::chrono::steady_clock::now()});
prune_if_dead(it);
}
/**
* @brief Liefert die aktuell geladene Entity für (Type, id), falls vorhanden.
*
* @tparam Type Entity-Typ.
* @param id Identifier/Primärschlüssel der Entity.
* @return @c shared_ptr auf die Entity oder ein leerer Pointer, falls nicht geladen/abgelaufen.
*
* @note Diese Methode ist threadsafe.
*/
template<typename Type>
std::shared_ptr<Type> get_entity(const utils::identifier &id) {
const auto k = make_key<Type>(id);
std::unique_lock lock(mutex_);
auto it = map_.find(k);
if (it == map_.end()) {
return {};
}
auto *entry = entry_cast_<Type>(it->second.get());
auto entity_ptr = entry->entity.lock();
if (entity_ptr) {
bus_.publish<object_cache_accessed_event>({k.type, id, std::chrono::steady_clock::now()});
}
prune_if_dead(it);
return entity_ptr;
}
/**
* @brief Prüft, ob für (Type, id) aktuell eine lebende Entity verfügbar ist.
*
* @tparam Type Entity-Typ.
* @param id Identifier/Primärschlüssel der Entity.
* @return @c true, wenn die Entity-Referenz nicht abgelaufen ist; sonst @c false.
*
* @note Diese Methode ist threadsafe.
*/
template<typename Type>
bool is_loaded(const utils::identifier &id) {
const auto k = make_key<Type>(id);
std::unique_lock lock(mutex_);
auto it = map_.find(k);
if (it == map_.end()) {
return false;
}
auto *entry = entry_cast_<Type>(it->second.get());
const bool loaded = !entry->entity.expired();
if (loaded) {
bus_.publish<object_cache_accessed_event>({k.type, id, std::chrono::steady_clock::now()});
}
prune_if_dead(it);
return loaded;
}
template<typename T>
void erase(utils::identifier id) {
const auto k = make_key<T>(id);
std::unique_lock lock(mutex_);
auto it = map_.find(k);
if (it == map_.end()) {
return;
}
auto *e = entry_cast_<T>(it->second.get());
if (auto p = e->proxy.lock()) {
p->invalidate();
}
map_.erase(it);
bus_.publish<object_cache_erased_event>({k.type, id, std::chrono::steady_clock::now()});
}
/**
* @brief Führt einen Cleanup-Lauf aus und entfernt Einträge, deren Proxy und Entity abgelaufen sind.
*
* @return Anzahl der entfernten Einträge.
*
* @note Diese Methode ist threadsafe.
*/
[[nodiscard]] std::size_t sweep() {
std::unique_lock lock(mutex_);
std::size_t removed = 0;
for (auto it = map_.begin(); it != map_.end(); ) {
if (it->second->is_dead()) {
it = map_.erase(it);
++removed;
} else {
++it;
}
}
if (removed > 0) {
bus_.publish<object_cache_sweep_event>({removed, std::chrono::steady_clock::now()});
}
return removed;
}
private:
struct key {
std::type_index type;
utils::identifier id{};
bool operator==(key const &other) const {
return type == other.type && id == other.id;
}
};
struct key_hash {
size_t operator()(key const &k) const noexcept {
// (3) robustere Hash-Kombination als XOR
size_t seed = std::hash<std::type_index>()(k.type);
const size_t h2 = std::hash<utils::identifier>()(k.id);
seed ^= h2 + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2);
return seed;
}
};
template<typename T>
static key make_key(const utils::identifier &id) {
return key{std::type_index(typeid(T)), id};
}
// (4) Debug-Absicherung für type-erased Casts
template<typename T>
static cache_entry<T> *entry_cast_(cache_entry_base *base) {
#ifndef NDEBUG
auto *p = dynamic_cast<cache_entry<T> *>(base);
assert(p && "object_cache: Type mismatch in cache entry (key/type invariant violated)");
return p;
#else
return static_cast<cache_entry<T> *>(base);
#endif
}
template<typename T, typename U>
using enable_if_resolver_shared_ptr_t = std::enable_if_t<std::is_base_of_v<object_resolver<T>, U>, int>;
template<typename T, typename U>
using enable_if_resolver_weak_ptr_t = std::enable_if_t<std::is_base_of_v<object_resolver<T>, U>, int>;
// shared_ptr<Derived> -> weak_ptr<Base>
template<typename T, typename U, enable_if_resolver_shared_ptr_t<T, U> = 0>
static std::weak_ptr<object_resolver<T>> to_weak(const std::shared_ptr<U> &s) {
return std::weak_ptr<object_resolver<T>>(std::static_pointer_cast<object_resolver<T>>(s));
}
// weak_ptr<Derived> -> weak_ptr<Base>
template<typename T, typename U, enable_if_resolver_weak_ptr_t<T, U> = 0>
static std::weak_ptr<object_resolver<T>> to_weak(const std::weak_ptr<U> &w) {
return std::weak_ptr<object_resolver<T>>(w);
}
// (2) Opportunistischer Cleanup: entferne Entry, wenn beide weak_ptr abgelaufen sind.
template<typename IteratorType>
bool prune_if_dead(IteratorType &it) {
if (it == map_.end()) {
return false;
}
if (it->second && it->second->is_dead()) {
map_.erase(it);
it = map_.end();
return true;
}
return false;
}
template<typename Type>
void prune_if_dead_typed(typename std::unordered_map<key, std::unique_ptr<cache_entry_base>, key_hash>::iterator &it) {
(void)Type{}; // T bleibt im Interface, damit bestehende Call-Sites unverändert bleiben können.
prune_if_dead(it);
}
private:
mutable std::shared_mutex mutex_{};
utils::message_bus &bus_;
std::unordered_map<key, std::unique_ptr<cache_entry_base>, key_hash> map_;
};
// --- Inline Anpassungen: typed prune nach Zugriff (damit wir T haben) ---
// ... existing code ...
// (Die typed prune wird oben benutzt; wir rufen sie direkt im Codepfad anstelle von prune_if_dead_)
} // namespace matador::object
#endif //MATADOR_OBJECT_CACHE_HPP
@@ -1,70 +0,0 @@
#ifndef QUERY_TABLE_DEFINITION_HPP
#define QUERY_TABLE_DEFINITION_HPP
#include "matador/object/attribute_definition.hpp"
#include <unordered_map>
namespace matador::object {
class object_definition final {
private:
using column_by_index = std::vector<attribute_definition>;
using column_index_pair = std::pair<std::reference_wrapper<attribute_definition>, column_by_index::difference_type>;
using column_by_name_map = std::unordered_map<std::string, column_index_pair>;
public:
using iterator = column_by_index::iterator;
using const_iterator = column_by_index::const_iterator;
object_definition() = default;
object_definition(std::initializer_list<attribute_definition> columns);
explicit object_definition(const std::vector<attribute_definition> &columns);
object_definition(const object_definition &x);
object_definition& operator=(const object_definition &x);
object_definition(object_definition&&) noexcept = default;
object_definition& operator=(object_definition&&) noexcept = default;
~object_definition() = default;
[[nodiscard]] bool has_primary_key() const;
[[nodiscard]] std::optional<attribute_definition> primary_key() const;
template < typename Type >
void append(const std::string &name, long size = -1) {
append(make_column<Type>(name, size));
}
void append(attribute_definition col);
[[nodiscard]] const std::vector<attribute_definition>& columns() const;
[[nodiscard]] const attribute_definition& at(const std::string &name) const;
[[nodiscard]] const attribute_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(attribute_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
+198
View File
@@ -0,0 +1,198 @@
#ifndef MATADOR_OBJECT_GENERATOR_HPP
#define MATADOR_OBJECT_GENERATOR_HPP
#include "matador/object/basic_repository.hpp"
#include "matador/object/object.hpp"
#include "matador/utils/access.hpp"
#include "matador/utils/primary_key_attribute.hpp"
#include <memory>
namespace matador::object {
class pk_type_determinator {
private:
pk_type_determinator() = default;
public:
template<class Type>
static utils::basic_type determine() {
pk_type_determinator determinator;
Type t;
access::process(determinator, t);
return determinator.type_;
}
template<typename ValueType>
void on_primary_key(const char *, ValueType &/*pk*/, const utils::primary_key_attribute& attr) {
type_ = utils::data_type_traits<ValueType>::type(attr.size());
}
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
template < class Type >
static void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/) {}
static void on_attribute(const char * /*id*/, char * /*x*/, const utils::field_attributes &/*attr*/) {}
template<class Pointer>
static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
template<class Pointer>
static void on_has_one(const char * /*id*/, Pointer &/*x*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType>
static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType>
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType>
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const utils::foreign_attributes &/*attr*/) {}
private:
utils::basic_type type_{};
};
class object_generator {
private:
explicit object_generator(basic_repository& repo, const std::shared_ptr<object> &object);
public:
template < class Type >
static std::shared_ptr<object> generate(basic_repository &repo, const std::string &name) {
return generate(std::make_unique<Type>(), repo, name);
}
template < class Type >
static std::shared_ptr<object> generate(std::unique_ptr<Type>&& t,
basic_repository &repo,
const std::string &name,
const std::string &join_column = "",
const std::string &inverse_join_column = "") {
const std::type_index ti(typeid(Type));
if (repo.has_object_for_type(ti)) {
auto obj = repo.object_for_type(ti);
repo.remove_object_for_type(ti);
obj->update_name(name);
return obj;
}
auto obj = std::make_shared<object>(name);
std::ignore = repo.provide_object_in_advance(ti, obj);
object_generator gen(repo, obj);
access::process(gen, *t);
if (!join_column.empty() && !inverse_join_column.empty()) {
gen.prepare_relation_table(join_column, inverse_join_column);
}
return obj;
}
template<typename BaseType>
static void on_base(const BaseType&) {}
template < class Type >
void on_primary_key(const char *, Type &x, const utils::primary_key_attribute& attr);
void on_revision(const char *id, uint64_t &rev);
template<typename Type>
void on_attribute(const char *id, Type &x, const utils::field_attributes &attr);
template<typename Type>
void on_attribute(const char *id, std::optional<Type> &x, const utils::field_attributes &attr);
template<class Pointer>
void on_belongs_to(const char *id, Pointer &x, const utils::foreign_attributes &/*attr*/) {
on_foreign_key(id, x);
create_fk_constraint<typename Pointer::value_type>(id);
}
template<class Pointer>
static void on_has_one(const char * /*id*/, Pointer &/*x*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {}
template <class Pointer>
void on_foreign_key(const char *id, Pointer &/*x*/) {
const auto type = pk_type_determinator::determine<typename Pointer::value_type>();
auto &ref = object_->attributes_.emplace_back(id, type, utils::constraints::ForeignKey, null_option_type::NotNull);
ref.index_ = object_->attributes_.size() - 1;
ref.owner_ = object_;
}
template<class ContainerType>
static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType>
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType>
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*cont*/, const utils::foreign_attributes &/*attr*/) {}
private:
template<typename ValueType>
attribute &emplace_attribute(const char *id, const utils::field_attributes& attr, null_option_type null_option) {
auto &ref = object_->attributes_.emplace_back(id, utils::data_type_traits<ValueType>::type(attr.size()), attr, null_option);
ref.index_ = object_->attributes_.size() - 1;
ref.owner_ = object_;
return ref;
}
void create_pk_constraint(const std::string& name) const;
template<typename Type>
void create_fk_constraint(const std::string& name) const;
void create_unique_constraint(const std::string& name) const;
[[nodiscard]] std::vector<attribute>::iterator find_attribute_by_name(const std::string &name) const;
void prepare_primary_key(const attribute &ref, utils::identifier &&pk) const;
void prepare_relation_table(const std::string &join_column, const std::string &inverse_join_column) const;
template<typename Type>
[[nodiscard]] std::shared_ptr<object> fk_object() const;
static std::shared_ptr<object> acquire_object(basic_repository &repo, const std::type_index &ti, const std::string& name);
private:
basic_repository &repo_;
std::shared_ptr<object> object_;
};
template<typename ValueType>
void object_generator::on_primary_key(const char *id, ValueType &x, const utils::primary_key_attribute& attr) {
auto cs = utils::constraints::PrimaryKey;
if (attr.generator() == utils::generator_type::Identity) {
cs |= utils::constraints::Identity;
}
auto &ref = emplace_attribute<ValueType>(id, { attr.size(), cs }, null_option_type::NotNull);
prepare_primary_key(ref, utils::identifier(x));
}
template<typename Type>
void object_generator::on_attribute(const char *id, Type &/*x*/, const utils::field_attributes &attr) {
std::ignore = emplace_attribute<Type>(id, attr, null_option_type::NotNull);
}
template<typename Type>
void object_generator::on_attribute(const char *id, std::optional<Type> & /*x*/, const utils::field_attributes &attr) {
std::ignore = emplace_attribute<Type>(id, attr, null_option_type::Nullable);
}
template<typename Type>
void object_generator::create_fk_constraint(const std::string& name) const {
const auto pk_attr = find_attribute_by_name(name);
if (pk_attr == std::end(object_->attributes_)) {
return;
}
const auto obj = fk_object<Type>();
restriction pk_constraint(pk_attr->index());
pk_constraint.options_ |= utils::constraints::ForeignKey;
pk_constraint.owner_ = object_;
pk_constraint.reference_ = obj;
object_->constraints_.emplace_back(std::move(pk_constraint));
}
template<typename Type>
std::shared_ptr<object> object_generator::fk_object() const {
const auto ti = std::type_index(typeid(Type));
if (const auto result = repo_.basic_info(ti)) {
return result->get().object();
}
if (repo_.has_object_for_type(ti)) {
return repo_.object_for_type(ti);
}
const auto obj = repo_.provide_object_in_advance(ti, std::make_shared<object>(""));
object_generator gen(repo_, obj);
Type t;
access::process(gen, t);
return obj;
}
}
#endif //MATADOR_OBJECT_GENERATOR_HPP
+34 -26
View File
@@ -2,49 +2,57 @@
#define OBJECT_INFO_HPP #define OBJECT_INFO_HPP
#include "matador/object/basic_object_info.hpp" #include "matador/object/basic_object_info.hpp"
#include "matador/object/object_definition.hpp" #include "matador/object/observer.hpp"
#include <functional>
namespace matador::object { namespace matador::object {
class schema_node; class repository_node;
template<typename Type> template<typename Type>
class object_info final : public basic_object_info { class object_info final : public basic_object_info {
public: public:
using create_func = std::function<std::unique_ptr<Type>()>; using create_func = std::function<std::unique_ptr<Type>()>;
object_info(const std::shared_ptr<schema_node>& node, object_info(const repository_node& node,
const std::shared_ptr<attribute_definition> &ref_column) const std::shared_ptr<class object> &obj,
: basic_object_info(node, {}, ref_column, {}) std::vector<std::unique_ptr<observer<Type>>>&& observers,
, creator_([]{return std::make_unique<Type>(); }){
}
object_info(const std::shared_ptr<schema_node>& node,
utils::identifier &&pk,
const std::shared_ptr<attribute_definition> &ref_column,
object_definition &&definition)
: basic_object_info(node, std::move(pk), ref_column, std::move(definition))
, creator_([]{return std::make_unique<Type>(); }){
}
object_info(const std::shared_ptr<schema_node>& node,
utils::identifier &&pk,
const std::shared_ptr<attribute_definition> &ref_column,
object_definition &&definition,
create_func&& creator) create_func&& creator)
: basic_object_info(node, std::move(pk), ref_column, std::move(definition)) : basic_object_info(node, obj)
, creator_(std::move(creator)){ , creator_(std::move(creator))
} , observers_(std::move(observers)){}
object_info(const std::shared_ptr<schema_node>& node,
object_definition &&definition, explicit object_info(const repository_node& node)
create_func&& creator) : basic_object_info(node, {}) {
: basic_object_info(node, std::move(definition))
, creator_(std::move(creator)){
} }
const Type &prototype() const { return prototype_; } const Type &prototype() const { return prototype_; }
std::unique_ptr<Type> create() const { return creator_(); } std::unique_ptr<Type> create() const { return creator_(); }
void on_attach() const override {
for (auto &observer : observers_) {
observer->on_attach(node_, prototype_);
}
}
void on_detach() const override {
for (auto &observer : observers_) {
observer->on_detach(node_, prototype_);
}
}
void register_observer(std::unique_ptr<observer<Type>>&& observer) {
observers_.push_back(std::move(observer));
}
const std::vector<std::unique_ptr<observer<Type>>>& observers() const {
return observers_;
}
private: private:
Type prototype_; Type prototype_;
create_func creator_{[]{ return std::make_unique<Type>(); }}; create_func creator_{[]{ return std::make_unique<Type>(); }};
std::vector<std::unique_ptr<observer<Type>>> observers_;
}; };
template<typename Type> template<typename Type>
+170 -22
View File
@@ -1,42 +1,190 @@
#ifndef OBJECT_PROXY_HPP #ifndef OBJECT_PROXY_HPP
#define OBJECT_PROXY_HPP #define OBJECT_PROXY_HPP
#include "matador/object/primary_key_resolver.hpp"
#include "matador/object/object_resolver.hpp"
#include <atomic>
#include <memory> #include <memory>
#include <utility>
#include <stdexcept>
#include <mutex>
namespace matador::object { namespace matador::object {
enum class object_state : uint8_t {
class basic_object_proxy { Transient,
public: Persistent,
virtual ~basic_object_proxy() = default; Detached,
Removed
[[nodiscard]] virtual void *get() const = 0;
}; };
template<class Type> template<class Type>
class object_proxy : public basic_object_proxy { class object_proxy final {
public: public:
object_proxy() = default; object_proxy() = default;
explicit object_proxy(Type* obj)
: obj_(obj) {}
explicit object_proxy(std::unique_ptr<Type> obj)
: obj_(std::move(obj)) {}
[[nodiscard]] void *get() const override { return static_cast<void*>(obj_.get()); } // Lazy
object_proxy(std::weak_ptr<object_resolver<Type>> resolver, utils::identifier id)
: resolver_(std::move(resolver))
, pk_(std::move(id))
, state_(object_state::Persistent) {
}
Type* operator->() const { return obj_.get(); } // Eager
Type& operator*() { return *obj_; } object_proxy(std::weak_ptr<object_resolver<Type>> resolver, std::shared_ptr<Type> obj)
const Type& operator*() const { return *obj_; } : obj_(std::move(obj))
, resolver_(std::move(resolver))
, pk_(obj_ ? primary_key_resolver::resolve_object(*obj_).pk : utils::identifier{})
, state_(obj_ ? object_state::Persistent : object_state::Detached) {
}
Type* pointer() const { return obj_.get(); } // Transient
explicit object_proxy(std::shared_ptr<Type> obj)
: obj_(std::move(obj))
, pk_(obj_ ? primary_key_resolver::resolve_object(*obj_).pk : utils::identifier{}) {
}
Type& ref() { return *obj_; } void attach(std::shared_ptr<Type> obj) {
const Type& ref() const { return *obj_; } std::lock_guard lock(mutex_);
void reset(Type* obj) { obj_.reset(obj); } obj_ = std::move(obj);
if (!obj_) {
pk_.clear();
state_ = object_state::Detached;
return;
}
pk_ = primary_key_resolver::resolve_object(*obj_).pk;
state_ = object_state::Persistent;
}
void resolver(std::weak_ptr<object_resolver<Type>> resolver) {
std::lock_guard lock(mutex_);
resolver_ = std::move(resolver);
}
[[nodiscard]] std::shared_ptr<Type> object() const {
return resolve_object();
}
void invalidate() {
std::lock_guard lock(mutex_);
obj_.reset();
resolver_.reset();
state_ = object_state::Detached;
}
[[nodiscard]] void *raw_pointer() const { return static_cast<void *>(pointer()); }
Type *operator->() {
auto *ptr = pointer();
if (!ptr) {
throw std::runtime_error("Cannot dereference empty object proxy");
}
return ptr;
}
const Type *operator->() const {
auto *ptr = pointer();
if (!ptr) {
throw std::runtime_error("Cannot dereference empty object proxy");
}
return ptr;
}
Type &operator*() {
auto *ptr = pointer();
if (!ptr) {
throw std::runtime_error("Cannot dereference empty object proxy");
}
return *ptr;
}
const Type &operator*() const {
auto *ptr = pointer();
if (!ptr) {
throw std::runtime_error("Cannot dereference empty object proxy");
}
return *ptr;
}
Type *pointer() const {
return resolve_object().get();
}
[[nodiscard]] bool empty() const {
std::lock_guard lock(mutex_);
return !obj_ && resolver_.expired();
}
[[nodiscard]] bool valid() const { return !empty(); }
[[nodiscard]] bool has_primary_key() const {
std::lock_guard lock(mutex_);
return !pk_.is_null();
}
[[nodiscard]] utils::identifier primary_key() const {
std::lock_guard lock(mutex_);
return pk_;
}
void primary_key(const utils::identifier &pk) {
std::lock_guard lock(mutex_);
pk_ = pk;
}
bool is_persistent() const { return is_state(object_state::Persistent); }
bool is_transient() const { return is_state(object_state::Transient); }
bool is_detached() const { return is_state(object_state::Detached); }
bool is_removed() const { return is_state(object_state::Removed); }
bool is_state(const object_state state) const {
std::lock_guard lock(mutex_);
return state_ == state;
}
void change_state(const object_state state) {
std::lock_guard lock(mutex_);
state_ = state;
}
private: private:
std::unique_ptr<Type> obj_{}; std::shared_ptr<Type> resolve_object() const {
}; std::shared_ptr<Type> current;
std::shared_ptr<object_resolver<Type>> resolver;
utils::identifier pk;
{
std::lock_guard lock(mutex_);
if (obj_) {
return obj_;
}
resolver = resolver_.lock();
if (!resolver) {
return nullptr;
}
pk = pk_;
}
current = resolver->resolve(pk);
{
std::lock_guard lock(mutex_);
if (!obj_) {
obj_ = std::move(current);
}
return obj_;
}
}
private:
mutable std::shared_ptr<Type> obj_{};
mutable std::weak_ptr<object_resolver<Type>> resolver_{};
utils::identifier pk_{};
object_state state_{object_state::Transient};
mutable std::mutex mutex_{};
};
} }
#endif //OBJECT_PROXY_HPP #endif //OBJECT_PROXY_HPP
+153 -29
View File
@@ -4,55 +4,179 @@
#include "matador/utils/identifier.hpp" #include "matador/utils/identifier.hpp"
#include "matador/object/object_proxy.hpp" #include "matador/object/object_proxy.hpp"
#include "matador/object/primary_key_resolver.hpp"
#include <memory> #include <memory>
namespace matador::object { namespace matador::object {
struct null_object_ptr_t {
constexpr null_object_ptr_t() = default;
};
inline constexpr null_object_ptr_t nullobj{};
template <typename Type> template <typename Type>
class object_ptr { class object_ptr {
public: public:
object_ptr() object_ptr()
: ptr_(std::make_shared<object_proxy<Type>>()) {} : proxy_(std::make_shared<object_proxy<Type>>()) {}
explicit object_ptr(Type *obj)
: ptr_(std::make_shared<object_proxy<Type>>(obj))
, pk_(primary_key_resolver::resolve_object(*obj).pk) {}
object_ptr(const object_ptr &other) : ptr_(other.ptr_) {}
object_ptr(object_ptr &&other) noexcept : ptr_(std::move(other.ptr_)) {}
object_ptr &operator=(const object_ptr &other) = default;
object_ptr &operator=(object_ptr &&other) = default;
using value_type = Type; object_ptr(null_object_ptr_t) {}
Type* operator->() const { return ptr_->pointer(); }
Type& operator*() { return ptr_->ref(); }
const Type& operator*() const { return ptr_->ref(); }
[[nodiscard]] bool empty() const { return ptr_->pointer() == nullptr; } explicit object_ptr(std::shared_ptr<Type> obj)
void reset(Type *obj) { : proxy_(std::make_shared<object_proxy<Type>>(std::move(obj))) {}
ptr_->reset(obj);
pk_ = primary_key_resolver::resolve_object(*obj).pk; explicit object_ptr(std::shared_ptr<object_proxy<Type>> obj)
: proxy_(std::move(obj)) {}
object_ptr(const object_ptr &other) = default;
object_ptr(object_ptr &&other) noexcept = default;
object_ptr& operator=(const object_ptr &other) = default;
object_ptr& operator=(object_ptr &&other) noexcept = default;
object_ptr& operator=(null_object_ptr_t) {
proxy_.reset();
return *this;
} }
Type* get() const { return static_cast<Type*>(ptr_->pointer()); } bool operator==(const object_ptr &other) const {
if (proxy_ == other.proxy_) {
return true;
}
operator bool() { return valid(); } if (!proxy_ || !other.proxy_) {
bool valid() { return ptr_ != nullptr; } return false;
}
if (has_primary_key() && other.has_primary_key()) {
return primary_key() == other.primary_key();
}
return false;
}
bool operator==(null_object_ptr_t) const {
return empty();
}
bool operator!=(const object_ptr &other) const { return !operator==(other); }
bool operator!=(null_object_ptr_t) const { return !empty(); }
using value_type = Type;
Type *operator->() const {
return checked_get();
}
Type &operator*() {
return *checked_get();
}
const Type &operator*() const {
return *checked_get();
}
[[nodiscard]] bool empty() const {
return proxy_ == nullptr || proxy_->empty();
}
Type *get() const {
return proxy_ ? proxy_->pointer() : nullptr;
}
[[nodiscard]] std::shared_ptr<Type> object() const {
return proxy_ ? proxy_->object() : nullptr;
}
void reset() {
proxy_.reset();
}
void reset(std::shared_ptr<object_proxy<Type>> proxy) {
proxy_ = std::move(proxy);
}
[[nodiscard]] std::shared_ptr<object_proxy<Type>> proxy() const {
return proxy_;
}
explicit operator bool() const {
return valid();
}
[[nodiscard]] bool valid() const {
return proxy_ != nullptr && !proxy_->empty();
}
[[nodiscard]] bool has_primary_key() const {
return proxy_ != nullptr && proxy_->has_primary_key();
}
[[nodiscard]] utils::identifier primary_key() const {
return proxy_ ? proxy_->primary_key() : utils::identifier{};
}
void primary_key(const utils::identifier &pk) {
ensure_proxy();
proxy_->primary_key(pk);
}
[[nodiscard]] bool is_persistent() const {
return proxy_ != nullptr && proxy_->is_persistent();
}
[[nodiscard]] bool is_transient() const {
return proxy_ != nullptr && proxy_->is_transient();
}
[[nodiscard]] bool is_detached() const {
return proxy_ != nullptr && proxy_->is_detached();
}
[[nodiscard]] bool is_removed() const {
return proxy_ != nullptr && proxy_->is_removed();
}
[[nodiscard]] bool is_state(const object_state state) const {
return proxy_ != nullptr && proxy_->is_state(state);
}
void change_state(object_state s) const {
if (proxy_) {
proxy_->change_state(s);
}
}
[[nodiscard]] const utils::identifier& primary_key() const { return pk_; }
void primary_key(const utils::identifier &pk) { pk_ = pk; }
private: private:
std::shared_ptr<object_proxy<Type>> ptr_{}; Type *checked_get() const {
utils::identifier pk_; auto *ptr = get();
if (!ptr) {
throw std::runtime_error("Cannot dereference empty object_ptr");
}
return ptr;
}
void ensure_proxy() {
if (!proxy_) {
proxy_ = std::make_shared<object_proxy<Type>>();
}
}
private:
std::shared_ptr<object_proxy<Type>> proxy_{};
}; };
template<typename> template<typename>
struct is_object_ptr : std::false_type struct is_object_ptr : std::false_type {
{}; };
template<typename Type> template<typename Type>
struct is_object_ptr<object_ptr<Type>> : std::true_type struct is_object_ptr<object_ptr<Type> > : std::true_type {
{}; };
template<class Type, typename... Args>
object_ptr<Type> make_object(Args &&... args) {
return object_ptr<Type>(std::make_shared<Type>(std::forward<Args>(args)...));
}
} }
#endif //OBJECT_PTR_HPP #endif //OBJECT_PTR_HPP
@@ -0,0 +1,30 @@
#ifndef MATADOR_OBJECT_LOADER_HPP
#define MATADOR_OBJECT_LOADER_HPP
#include "matador/object/abstract_type_resolver.hpp"
#include "matador/object/abstract_joined_resolver.hpp"
#include <memory>
namespace matador::utils {
class identifier;
}
namespace matador::object {
template<typename Type>
class object_resolver : public abstract_type_resolver {
public:
object_resolver() : abstract_type_resolver(typeid(Type)) {}
virtual std::shared_ptr<Type> resolve(const utils::identifier& id) = 0;
};
template<typename Type>
class joined_object_resolver : public abstract_joined_resolver, public object_resolver<Type> {
public:
joined_object_resolver(const std::type_index& root_type, const std::string& join_column)
: abstract_joined_resolver(root_type, typeid(Type), join_column) {}
std::shared_ptr<Type> resolve(const utils::identifier& id) override = 0;
};
}
#endif //MATADOR_OBJECT_LOADER_HPP
@@ -0,0 +1,21 @@
#ifndef MATADOR_OBJECT_RESOLVER_FACTORY_HPP
#define MATADOR_OBJECT_RESOLVER_FACTORY_HPP
#include "matador/object/abstract_type_resolver_factory.hpp"
#include "matador/object/object_resolver.hpp"
namespace matador::object {
class object_resolver_factory : public abstract_type_resolver_factory {
public:
template<class Type>
[[nodiscard]] std::shared_ptr<object_resolver<Type>> resolver() const {
const auto res = acquire_object_resolver(std::type_index(typeid(Type)));
if (!res) {
return std::dynamic_pointer_cast<object_resolver<Type>>(res);
}
return std::dynamic_pointer_cast<object_resolver<Type>>(res);
}
};
}
#endif //MATADOR_OBJECT_RESOLVER_FACTORY_HPP
+124
View File
@@ -0,0 +1,124 @@
#ifndef MATADOR_OBSERVER_HPP
#define MATADOR_OBSERVER_HPP
#include <typeindex>
namespace matador::object {
class repository_node;
class abstract_observer {
public:
virtual ~abstract_observer() = default;
protected:
/**
* Constructs a basic_object_store_observer for the
* give std::type_index.
*
* @param ti type index of the observer
*/
explicit abstract_observer(const std::type_index &ti);
public:
/**
* Return the type index of the observer.
*
* @return The type index of the observer.
*/
[[nodiscard]] const std::type_index& index() const;
private:
std::type_index type_index_;
};
/**
* @class observer
* @tparam Type Type of the observer
* @brief Base class for typed observer classes
*
* When interested in observe
* - attach (prototype_node)
* - detach (prototype_node)
* - insert (an object)
* - update (an object)
* - delete (an object)
* actions an observer class instance must be
* registered within the repository.
* Use this class as a base class for all observer classes.
*/
template < class Type >
class observer : public abstract_observer {
public:
/**
* Default constructor
*/
observer() : abstract_observer(std::type_index(typeid(Type))) {}
/**
* @brief Called on repository_node attachment
*
* When a prototype node is attached to the repository,
* this is called after the attaching succeeded.
*
* @param node The attached repository_node
* @param prototype The prototype object of the attached node
*/
virtual void on_attach(const repository_node &node, const Type &prototype) const = 0;
/**
* @brief Called on repository_node detached
*
* When a prototype node is detached from the repository,
* this is called before the detaching succeeded.
*
* @param node The to be detached repository_node
* @param prototype The prototype object of the detached node
*/
virtual void on_detach(const repository_node &node, const Type &prototype) const = 0;
/**
* @brief Called on object insertion.
*
* Called when an object is inserted
* into the repository.
*
* @param obj The proxy of the inserted object.
*/
virtual void on_insert(const Type &obj) = 0;
/**
* @brief Called on object update.
*
* Called when an object is updated
* in the repository.
*
* @param obj The proxy of the updated object.
*/
virtual void on_update(const Type &obj) = 0;
/**
* @brief Called on object deletion.
*
* Called when an object is deleted
* from the repository.
*
* @param obj The proxy of the deleted object.
*/
virtual void on_delete(const Type &obj) = 0;
};
namespace internal {
template < class Type >
class null_observer : public observer<Type> {
public:
template < class OtherType >
explicit null_observer(const null_observer<OtherType> *) {}
void on_attach(const repository_node &, const Type &) const override {}
void on_detach(const repository_node &, const Type &) const override {}
void on_insert(Type &) override {}
void on_update(Type &) override {}
void on_delete(Type &) override {}
};
}
}
#endif //MATADOR_OBSERVER_HPP
+133
View File
@@ -0,0 +1,133 @@
#ifndef MATADOR_PK_FIELD_LOCATOR_HPP
#define MATADOR_PK_FIELD_LOCATOR_HPP
#include "matador/utils/primary_key_attribute.hpp"
#include "matador/utils/field_attributes.hpp"
#include "matador/utils/foreign_attributes.hpp"
#include <variant>
#include <array>
#include <stdexcept>
#include <string>
namespace matador::object::detail {
using uuid16 = std::array<std::uint8_t, 16>;
using pk_value = std::variant<std::int64_t, std::string, uuid16>;
enum class pk_kind { integer, string, uuid };
struct pk_field_descriptor {
std::size_t offset{};
pk_kind kind{pk_kind::integer};
bool (*is_known_at)(const void* obj, std::size_t offset) = nullptr;
pk_value (*get_at)(const void* obj, std::size_t offset) = nullptr;
void (*set_at)(void* obj, std::size_t offset, const pk_value& v) = nullptr;
};
template < typename Type >
struct pk_field_locator {
Type *base{nullptr};
pk_field_descriptor desc{};
bool found{false};
explicit pk_field_locator (Type &obj)
: base(&obj) {}
template<typename BaseType>
static void on_base(const BaseType&) {}
template <typename PrimaryKeyType>
void on_primary_key(const char *, PrimaryKeyType &pk, const utils::primary_key_attribute & /*attr*/) {
// Offset bestimmen
const auto* obj_bytes = reinterpret_cast<std::uint8_t*>(base);
const auto* pk_bytes = reinterpret_cast<std::uint8_t*>(&pk);
desc.offset = static_cast<std::size_t>(pk_bytes - obj_bytes);
if constexpr (std::is_integral_v<PrimaryKeyType> || std::is_enum_v<PrimaryKeyType>) {
desc.kind = pk_kind::integer;
desc.is_known_at = [](void *obj, const std::size_t off) -> bool {
auto *p = reinterpret_cast<PrimaryKeyType *>(static_cast<std::uint8_t *>(obj) + off);
return *p != PrimaryKeyType{};
};
desc.get_at = [](void *obj, const std::size_t off) -> pk_value {
auto *p = reinterpret_cast<PrimaryKeyType *>(static_cast<std::uint8_t *>(obj) + off);
return static_cast<std::int64_t>(*p);
};
desc.set_at = [](void *obj, const std::size_t off, const pk_value &v) {
auto *p = reinterpret_cast<PrimaryKeyType *>(static_cast<std::uint8_t *>(obj) + off);
if (auto *i = std::get_if<std::int64_t>(&v)) {
*p = static_cast<PrimaryKeyType>(*i);
} else {
throw std::logic_error("PK type mismatch: expected integer");
}
};
} else if constexpr (std::is_same_v<PrimaryKeyType, std::string>) {
desc.kind = pk_kind::string;
desc.is_known_at = [](void *obj, const std::size_t off) -> bool {
const auto *p = reinterpret_cast<std::string *>(static_cast<std::uint8_t *>(obj) + off);
return !p->empty();
};
desc.get_at = [](void *obj, const std::size_t off) -> pk_value {
auto *p = reinterpret_cast<std::string *>(static_cast<std::uint8_t *>(obj) + off);
return *p;
};
desc.set_at = [](void *obj, const std::size_t off, const pk_value &v) {
auto *p = reinterpret_cast<std::string *>(static_cast<std::uint8_t *>(obj) + off);
if (auto *s = std::get_if<std::string>(&v)) {
*p = *s;
} else {
throw std::logic_error("PK type mismatch: expected string");
}
};
} else if constexpr (std::is_same_v<PrimaryKeyType, uuid16>) {
desc.kind = pk_kind::uuid;
desc.is_known_at = [](void *obj, const std::size_t off) -> bool {
const auto *p = reinterpret_cast<uuid16 *>(static_cast<std::uint8_t *>(obj) + off);
// “unknown” = all zeros
for (const auto b: *p) {
if (b != 0) return true;
}
return false;
};
desc.get_at = [](void *obj, const std::size_t off) -> pk_value {
auto *p = reinterpret_cast<uuid16 *>(static_cast<std::uint8_t *>(obj) + off);
return *p;
};
desc.set_at = [](void *obj, const std::size_t off, const pk_value &v) {
auto *p = reinterpret_cast<uuid16 *>(static_cast<std::uint8_t *>(obj) + off);
if (auto *u = std::get_if<uuid16>(&v)) {
*p = *u;
} else {
throw std::logic_error("PK type mismatch: expected uuid16");
}
};
} else {
throw std::logic_error("Unsupported primary key field type");
}
found = true;
}
static void on_revision(const char *, std::uint64_t &) {}
template <typename V>
static void on_attribute(const char *, V &, const utils::field_attributes &/*attr*/) {}
template <typename Pointer>
static void on_belongs_to(const char *, Pointer &, const utils::foreign_attributes &) {}
template <typename Pointer>
static void on_has_one(const char *, Pointer &, const char * /*join_column*/, const utils::foreign_attributes &) {}
template <typename Container>
static void on_has_many(const char *, Container &, const char *, const utils::foreign_attributes &) {}
template <typename Container>
static void on_has_many_to_many(const char *, Container &, const char *, const char *,const utils::foreign_attributes &) {}
template <typename Container>
static void on_has_many_to_many(const char *, Container &, const utils::foreign_attributes &) {}
};
}
#endif //MATADOR_PK_FIELD_LOCATOR_HPP
@@ -3,14 +3,15 @@
#include "matador/utils/access.hpp" #include "matador/utils/access.hpp"
#include "matador/utils/field_attributes.hpp" #include "matador/utils/field_attributes.hpp"
#include "matador/utils/foreign_attributes.hpp"
#include "matador/utils/identifier.hpp" #include "matador/utils/identifier.hpp"
#include "matador/utils/primary_key_attribute.hpp" #include "matador/utils/primary_key_attribute.hpp"
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include <type_traits>
namespace matador::utils {
class foreign_attributes;
}
namespace matador::object { namespace matador::object {
@@ -42,8 +43,10 @@ public:
return resolver.resolve(obj); return resolver.resolve(obj);
} }
template<typename BaseType>
static void on_base(const BaseType&) {}
template < class Type > template < class Type >
void on_primary_key(const char *id, Type &pk, const utils::primary_key_attribute& attr = utils::default_pk_attributes) { void on_primary_key(const char *id, Type &pk, const utils::primary_key_attribute& attr) {
primary_key_info_.pk_column_name = id; primary_key_info_.pk_column_name = id;
primary_key_info_.type = utils::data_type_traits<Type>::type(attr.size()); primary_key_info_.type = utils::data_type_traits<Type>::type(attr.size());
primary_key_info_.pk = pk; primary_key_info_.pk = pk;
@@ -51,11 +54,11 @@ public:
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {} static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
template<typename Type> template<typename Type>
static void on_attribute(const char * /*id*/, Type &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} static void on_attribute(const char * /*id*/, Type &/*val*/, const utils::field_attributes &/*attr*/) {}
template<class Pointer> template<class Pointer>
static void on_belongs_to(const char * /*id*/, Pointer &/*val*/, const utils::foreign_attributes &/*attr*/) {} static void on_belongs_to(const char * /*id*/, Pointer &/*val*/, const utils::foreign_attributes &/*attr*/) {}
template<class Pointer> template<class Pointer>
static void on_has_one(const char * /*id*/, Pointer &/*val*/, const utils::foreign_attributes &/*attr*/) {} static void on_has_one(const char * /*id*/, Pointer &/*val*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
static void on_has_many(const char * /*id*/, ContainerType &/*col*/, const char *, const utils::foreign_attributes &/*attr*/) {} static void on_has_many(const char * /*id*/, ContainerType &/*col*/, const char *, const utils::foreign_attributes &/*attr*/) {}
template<class ContainerType> template<class ContainerType>
+195 -159
View File
@@ -1,11 +1,11 @@
#ifndef RELATION_COMPLETER_HPP #ifndef RELATION_COMPLETER_HPP
#define RELATION_COMPLETER_HPP #define RELATION_COMPLETER_HPP
#include "matador/object/internal/shadow_schema.hpp" #include "matador/object/basic_repository.hpp"
#include "matador/object/foreign_node_completer.hpp"
#include "matador/object/many_to_many_relation.hpp" #include "matador/object/many_to_many_relation.hpp"
#include "matador/object/join_columns_collector.hpp" #include "matador/object/join_columns_collector.hpp"
#include "matador/object/object_ptr.hpp" #include "matador/object/repository_node.hpp"
#include "matador/object/schema_node.hpp"
#include "matador/logger/log_manager.hpp" #include "matador/logger/log_manager.hpp"
@@ -28,20 +28,22 @@ public:
return finder.found_; return finder.found_;
} }
template<typename BaseType>
static void on_base(const BaseType&) {}
template<class PrimaryKeyType> template<class PrimaryKeyType>
static void on_primary_key(const char * /*id*/, PrimaryKeyType &/*pk*/, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {} static void on_primary_key(const char * /*id*/, PrimaryKeyType &/*pk*/, const utils::primary_key_attribute& /*attr*/) {}
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {} static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
template<typename AttributeType> template<typename AttributeType>
static void on_attribute(const char * /*id*/, AttributeType &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} static void on_attribute(const char * /*id*/, AttributeType &/*val*/, const utils::field_attributes &/*attr*/) {}
template<typename AttributeType> template<typename AttributeType>
static void on_attribute(const char * /*id*/, std::optional<AttributeType> &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} static void on_attribute(const char * /*id*/, std::optional<AttributeType> &/*val*/, const utils::field_attributes &/*attr*/) {}
template<class ForeignPointerType> template<class ForeignPointerType>
void on_belongs_to(const char *id, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/) { void on_belongs_to(const char *id, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/) {
found_ = requested_join_column_ == id; found_ = requested_join_column_ == id;
} }
template<class ForeignPointerType> template<class ForeignPointerType>
static void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/) {} static void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {}
template<class CollectionType> template<class CollectionType>
static void on_has_many(const char * /*id*/, CollectionType &, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {} static void on_has_many(const char * /*id*/, CollectionType &, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {}
@@ -91,33 +93,31 @@ private:
* no has_many or belongs_to * no has_many or belongs_to
* invalid relation -> error * invalid relation -> error
*/ */
template<typename Type> template<typename Type, template<typename> typename... Observers>
class relation_completer final { class relation_completer final {
private:
using node_ptr = std::shared_ptr<schema_node>;
public: public:
using endpoint_ptr = std::shared_ptr<relation_endpoint>; using endpoint_ptr = std::shared_ptr<relation_endpoint>;
static void complete(const std::shared_ptr<schema_node> &node) { static void complete(repository_node *node, const std::vector<std::unique_ptr<observer<Type>>> &observers) {
internal::shadow_schema shadow(node->schema_); relation_completer completer(node->repo_, observers);
relation_completer completer(shadow);
completer.complete_node_relations(node); completer.complete_node_relations(node);
} }
template<typename BaseType>
static void on_base(const BaseType&) {}
template<class PrimaryKeyType> template<class PrimaryKeyType>
static void on_primary_key(const char * /*id*/, PrimaryKeyType &/*pk*/, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {} static void on_primary_key(const char * /*id*/, PrimaryKeyType &/*pk*/, const utils::primary_key_attribute& /*attr*/) {}
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {} static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
template<typename AttributeType> template<typename AttributeType>
static void on_attribute(const char * /*id*/, AttributeType &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} static void on_attribute(const char * /*id*/, AttributeType &/*val*/, const utils::field_attributes &/*attr*/) {}
template<typename AttributeType> template<typename AttributeType>
static void on_attribute(const char * /*id*/, std::optional<AttributeType> &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {} static void on_attribute(const char * /*id*/, std::optional<AttributeType> &/*val*/, const utils::field_attributes &/*attr*/) {}
template<class ForeignPointerType> template<class ForeignPointerType>
void on_belongs_to(const char *id, ForeignPointerType &obj, const utils::foreign_attributes &attr); void on_belongs_to(const char *id, ForeignPointerType &obj, const utils::foreign_attributes &attr);
template<class ForeignPointerType> template<class ForeignPointerType>
void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/); void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/);
template<class CollectionType> template<class CollectionType>
void on_has_many(const char *id, CollectionType &, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t<is_object_ptr<typename CollectionType::value_type>::value> * = nullptr); void on_has_many(const char *id, CollectionType &, const char *join_column, const utils::foreign_attributes &attr, std::enable_if_t<is_object_ptr<typename CollectionType::value_type>::value> * = nullptr);
@@ -130,12 +130,12 @@ public:
void on_has_many_to_many(const char *id, CollectionType &collection, const utils::foreign_attributes &attr); void on_has_many_to_many(const char *id, CollectionType &collection, const utils::foreign_attributes &attr);
private: private:
explicit relation_completer(internal::shadow_schema &shadow) explicit relation_completer(basic_repository &repo, const std::vector<std::unique_ptr<observer<Type>>>& observers)
: schema_(shadow) : repo_(repo)
, log_(logger::create_logger("relation_completer")) { , log_(logger::create_logger("relation_completer"))
} , observers_(observers) {}
void complete_node_relations(const std::shared_ptr<schema_node> &node) { void complete_node_relations(repository_node* node) {
nodes_.push(node); nodes_.push(node);
Type obj; Type obj;
@@ -143,21 +143,27 @@ private:
nodes_.pop(); nodes_.pop();
} }
template<typename NodeType>
void attach_relation_node(const std::string &name, const std::string &join_column, const std::string &inverse_join_column);
static void register_relation_endpoints(const endpoint_ptr &endpoint, static void register_relation_endpoints(const endpoint_ptr &endpoint,
const endpoint_ptr &other_endpoint); const endpoint_ptr &other_endpoint);
static void link_relation_endpoints(const endpoint_ptr &endpoint, static void link_relation_endpoints(const endpoint_ptr &endpoint,
const endpoint_ptr &other_endpoint); const endpoint_ptr &other_endpoint);
[[nodiscard]] repository_node* find_node(const std::type_index &ti) const;
private: private:
std::stack<node_ptr> nodes_; std::stack<repository_node*> nodes_;
internal::shadow_schema &schema_; basic_repository &repo_;
logger::logger log_; logger::logger log_;
join_columns_collector join_columns_collector_{}; join_columns_collector join_columns_collector_{};
const std::vector<std::unique_ptr<observer<Type>>>& observers_;
}; };
template<typename Type> template<typename Type, template<typename> typename... Observers>
template<class CollectionType> template<class CollectionType>
void relation_completer<Type>::on_has_many(const char *id, CollectionType &, void relation_completer<Type, Observers...>::on_has_many(const char *id, CollectionType &,
const char *join_column, const char *join_column,
const utils::foreign_attributes &, const utils::foreign_attributes &,
std::enable_if_t<is_object_ptr<typename CollectionType::value_type>::value> * /*unused*/) { std::enable_if_t<is_object_ptr<typename CollectionType::value_type>::value> * /*unused*/) {
@@ -165,132 +171,99 @@ void relation_completer<Type>::on_has_many(const char *id, CollectionType &,
using value_type = typename CollectionType::value_type::value_type; using value_type = typename CollectionType::value_type::value_type;
using relation_value_type = many_to_many_relation<Type, value_type>; using relation_value_type = many_to_many_relation<Type, value_type>;
// Check if the object_ptr type is already inserted in the schema (by id) // Check if the object_ptr type is already inserted in the schema (by id)
auto result = schema_.find_node(typeid(value_type)); const auto foreign_node = find_node(typeid(value_type));
if (!result) { if (!foreign_node) {
// Todo: throw internal error or attach node // Todo: throw internal error or attach node
return; return;
} }
const auto foreign_node = result.value(); // has foreign node corresponding join column (join_column)
// has foreign node corresponding join column (join_column)
if (const auto it = foreign_node->info_->find_relation_endpoint(typeid(Type)); it != foreign_node->info().endpoint_end()) { if (const auto it = foreign_node->info_->find_relation_endpoint(typeid(Type)); it != foreign_node->info().endpoint_end()) {
// corresponding belongs_to is available and was called (has_many <-> belongs_to) // corresponding belongs_to is available and was called (has_many <-> belongs_to)
// complete the relation // complete the relation
const auto local_endpoint = std::make_shared<relation_endpoint>(id, relation_type::HAS_MANY, foreign_node); const auto local_endpoint = std::make_shared<relation_endpoint>(id, relation_type::HasMany, *foreign_node);
nodes_.top()->info_->register_relation_endpoint(typeid(value_type), local_endpoint); nodes_.top()->info_->register_relation_endpoint(typeid(value_type), local_endpoint);
link_relation_endpoints(local_endpoint, it->second); link_relation_endpoints(local_endpoint, it->second);
} else if (join_column_finder::has_join_column<value_type>(join_column)) { } else if (join_column_finder::has_join_column<value_type>(join_column)) {
// corresponding belongs_to is available but was not called (has_many <-> belongs_to) // corresponding belongs_to is available but was not called (has_many <-> belongs_to)
// prepare the relation // prepare the relation
const auto local_endpoint = std::make_shared<relation_endpoint>(id, relation_type::HAS_MANY, foreign_node); const auto local_endpoint = std::make_shared<relation_endpoint>(id, relation_type::HasMany, *foreign_node);
nodes_.top()->info_->register_relation_endpoint(typeid(value_type), local_endpoint); nodes_.top()->info_->register_relation_endpoint(typeid(value_type), local_endpoint);
} else { } else {
// A relation table is necessary // A relation table is necessary.
// Endpoint was not found. // Endpoint was not found.
// Always attach a many-to-many relation type. If later a // Always attach a many-to-many relation type. If later a
// belongs-to relation handles this relation, the many-to-many // belongs-to relation handles this relation, the many-to-many
// relation is maybe detached. // relation is maybe detached.
log_.debug("node '%s' has has many foreign keys '%s' mapped by '%s'", nodes_.top()->name().c_str(), id, join_column); log_.debug("node '%s' has has many foreign keys '%s' mapped by '%s'", nodes_.top()->name().c_str(), id, join_column);
result = schema_node::make_relation_node<relation_value_type>( auto node = repository_node::make_node<relation_value_type>(repo_, id, [join_column] {
schema_.schema(), id, [join_column] { return std::make_unique<relation_value_type>("id", join_column);
return std::make_unique<relation_value_type>("id", join_column); }, {});
}); const auto result = repo_.attach_node(node.release(), "");
if (!result) {
// Todo: throw internal error
return;
}
const auto local_endpoint = std::make_shared<relation_endpoint>(id, relation_type::HAS_MANY, result.value());
const auto foreign_endpoint = std::make_shared<relation_endpoint>("id", relation_type::BELONGS_TO, nodes_.top());
nodes_.top()->info_->register_relation_endpoint(typeid(value_type), local_endpoint);
result.value()->info_->register_relation_endpoint(nodes_.top()->type_index(), foreign_endpoint);
link_relation_endpoints(local_endpoint, foreign_endpoint);
const auto foreign_value_endpoint = std::make_shared<relation_endpoint>(join_column, relation_type::BELONGS_TO, foreign_node);
result.value()->info_->register_relation_endpoint(typeid(value_type), foreign_value_endpoint);
}
}
template<typename Type>
template<class CollectionType>
void relation_completer<Type>::on_has_many(const char *id, CollectionType &, const char *join_column,
const utils::foreign_attributes &,
std::enable_if_t<!is_object_ptr<typename CollectionType::value_type>::value>
* /*unused*/) {
using value_type = typename CollectionType::value_type;
using relation_value_type = many_to_relation<Type, value_type>;
const auto result = schema_node::make_relation_node<relation_value_type>(
schema_.schema(), id, [join_column] {
return std::make_unique<relation_value_type>(join_column, "value");
});
if (!result) {
// Todo: throw internal exception
return;
}
const auto local_endpoint = std::make_shared<relation_endpoint>(id, relation_type::HAS_MANY, result.value());
const auto foreign_endpoint = std::make_shared<relation_endpoint>(join_column, relation_type::BELONGS_TO, nodes_.top());
nodes_.top()->info_->register_relation_endpoint(typeid(value_type), local_endpoint);
result.value()->info_->register_relation_endpoint(nodes_.top()->type_index(), foreign_endpoint);
link_relation_endpoints(local_endpoint, foreign_endpoint);
}
template<typename Type>
template<class CollectionType>
void relation_completer<Type>::on_has_many_to_many(const char *id,
CollectionType &/*collection*/,
const char *join_column,
const char *inverse_join_column,
const utils::foreign_attributes &/*attr*/) {
using relation_value_type = many_to_many_relation<typename CollectionType::value_type::value_type, Type>;
using value_type = typename CollectionType::value_type::value_type;
// Check if the object_ptr type is already inserted in the schema (by id)
auto result = schema_.find_node(typeid(value_type));
if (!result) {
// Todo: throw internal error or attach node
return;
}
const auto foreign_node = result.value();
result = schema_.find_node(id);
if (!result) {
// Relation not found.
auto creator = [join_column, inverse_join_column] {
return std::make_unique<relation_value_type>(join_column, inverse_join_column);
};
result = schema_node::make_relation_node<relation_value_type>(schema_.schema(), id, std::move(creator));
if (!result) { if (!result) {
// Todo: throw internal error // Todo: throw internal error
return; return;
} }
auto& node = result.value(); auto *attached_node = result.value();
const auto local_endpoint = std::make_shared<relation_endpoint>(id, relation_type::HAS_MANY, node); const auto local_endpoint = std::make_shared<relation_endpoint>(id, relation_type::HasMany, *attached_node);
const auto join_endpoint = std::make_shared<relation_endpoint>(join_column, relation_type::BELONGS_TO, nodes_.top()); const auto foreign_endpoint = std::make_shared<relation_endpoint>("id", relation_type::BelongsTo, *nodes_.top());
const auto inverse_join_endpoint = std::make_shared<relation_endpoint>(inverse_join_column, relation_type::BELONGS_TO, foreign_node); nodes_.top()->info_->register_relation_endpoint(typeid(value_type), local_endpoint);
const auto foreign_endpoint = std::make_shared<relation_endpoint>(id, relation_type::HAS_MANY, node); attached_node->info_->register_relation_endpoint(nodes_.top()->type_index(), foreign_endpoint);
// register relation endpoint in local node link_relation_endpoints(local_endpoint, foreign_endpoint);
nodes_.top()->info_->register_relation_endpoint(typeid(typename CollectionType::value_type::value_type), local_endpoint);
// register relation endpoint in foreign node
foreign_node->info_->register_relation_endpoint(nodes_.top()->type_index(), foreign_endpoint);
// register endpoints in relation node
node->info_->register_relation_endpoint(nodes_.top()->type_index(), join_endpoint);
node->info_->register_relation_endpoint(typeid(typename CollectionType::value_type::value_type), inverse_join_endpoint);
// link endpoints const auto foreign_value_endpoint = std::make_shared<relation_endpoint>(join_column, relation_type::BelongsTo, *foreign_node);
link_relation_endpoints(local_endpoint, join_endpoint); attached_node->info_->register_relation_endpoint(typeid(value_type), foreign_value_endpoint);
link_relation_endpoints(foreign_endpoint, inverse_join_endpoint);
} }
} }
template<typename Type> template<typename Type, template<typename> typename... Observers>
template<class CollectionType> template<class CollectionType>
void relation_completer<Type>::on_has_many_to_many(const char *id, void relation_completer<Type, Observers...>::on_has_many(const char *id, CollectionType &, const char *join_column,
const utils::foreign_attributes &,
std::enable_if_t<!is_object_ptr<typename CollectionType::value_type>::value>* /*unused*/) {
using value_type = typename CollectionType::value_type;
using relation_value_type = many_to_relation<Type, value_type>;
auto observers = internal::observer_list_copy_creator<Type, relation_value_type, Observers...>::copy_create(observers_);
auto node = repository_node::make_node<relation_value_type>(repo_, id, [join_column] {
return std::make_unique<relation_value_type>(join_column, "value");
}, std::move(observers));
const auto result = repo_.attach_node(node.release(), "");
if (!result) {
// Todo: throw internal exception
return;
}
auto *attached_node = result.value();
const auto local_endpoint = std::make_shared<relation_endpoint>(id, relation_type::HasMany, *attached_node);
const auto foreign_endpoint = std::make_shared<relation_endpoint>(join_column, relation_type::BelongsTo, *nodes_.top());
nodes_.top()->info_->register_relation_endpoint(typeid(value_type), local_endpoint);
attached_node->info_->register_relation_endpoint(nodes_.top()->type_index(), foreign_endpoint);
link_relation_endpoints(local_endpoint, foreign_endpoint);
}
template<typename Type, template<typename> typename... Observers>
template<class CollectionType>
void relation_completer<Type, Observers...>::on_has_many_to_many(const char *id,
CollectionType &/*collection*/,
const char *join_column,
const char *inverse_join_column,
const utils::foreign_attributes &/*attr*/) {
if (!repo_.expecting_relation_node(id)) {
repo_.expect_relation_node(id, typeid(typename CollectionType::value_type::value_type));
} else {
attach_relation_node<typename CollectionType::value_type::value_type>(id, join_column, inverse_join_column);
repo_.remove_expected_relation_node(id);
}
}
template<typename Type, template<typename> typename... Observers>
template<class CollectionType>
void relation_completer<Type, Observers...>::on_has_many_to_many(const char *id,
CollectionType &collection, CollectionType &collection,
const utils::foreign_attributes &attr) { const utils::foreign_attributes &attr) {
const auto join_columns = join_columns_collector_.collect<typename CollectionType::value_type::value_type>(); const auto join_columns = join_columns_collector_.collect<typename CollectionType::value_type::value_type>();
@@ -302,55 +275,53 @@ void relation_completer<Type>::on_has_many_to_many(const char *id,
attr); attr);
} }
template<typename Type> template<typename Type, template<typename> typename... Observers>
template<class ForeignPointerType> template<class ForeignPointerType>
void relation_completer<Type>::on_has_one(const char *id, void relation_completer<Type, Observers...>::on_has_one(const char *id,
ForeignPointerType &/*obj*/, ForeignPointerType &/*obj*/,
const char * /*join_column*/,
const utils::foreign_attributes &/*attr*/) { const utils::foreign_attributes &/*attr*/) {
using value_type = typename ForeignPointerType::value_type; using value_type = typename ForeignPointerType::value_type;
const auto ti = std::type_index(typeid(value_type)); const auto foreign_node = find_node(typeid(value_type));
const auto result = schema_.find_node(ti); if (!foreign_node) {
if (!result) { // Todo: throw internal error or attach node
// Node was not found
// Todo: Throw internal error
return; return;
} }
auto local_it = nodes_.top()->info().find_relation_endpoint(typeid(value_type)); auto local_it = nodes_.top()->info().find_relation_endpoint(typeid(value_type));
if (local_it == nodes_.top()->info().endpoint_end()) { if (local_it == nodes_.top()->info().endpoint_end()) {
const auto local_endpoint = std::make_shared<relation_endpoint>(id, relation_type::HAS_ONE, result.value()); const auto local_endpoint = std::make_shared<relation_endpoint>(id, relation_type::HasOne, *foreign_node);
local_it = nodes_.top()->info_->register_relation_endpoint(typeid(value_type), local_endpoint); local_it = nodes_.top()->info_->register_relation_endpoint(typeid(value_type), local_endpoint);
} }
if (const auto foreign_it = result.value()->info().find_relation_endpoint(typeid(Type)); foreign_it != result.value()->info().endpoint_end()) { if (const auto foreign_it = foreign_node->info().find_relation_endpoint(typeid(Type)); foreign_it != foreign_node->info().endpoint_end()) {
link_relation_endpoints(local_it->second, foreign_it->second); link_relation_endpoints(local_it->second, foreign_it->second);
} }
} }
template<typename Type> template<typename Type, template<typename> typename... Observers>
template<class ForeignPointerType> template<class ForeignPointerType>
void relation_completer<Type>::on_belongs_to(const char *id, void relation_completer<Type, Observers...>::on_belongs_to(const char *id,
ForeignPointerType & /*obj*/, ForeignPointerType & /*obj*/,
const utils::foreign_attributes & /*attr*/) { const utils::foreign_attributes & /*attr*/) {
using value_type = typename ForeignPointerType::value_type; using value_type = typename ForeignPointerType::value_type;
const auto ti = std::type_index(typeid(value_type)); const auto ti = std::type_index(typeid(value_type));
auto result = schema_.find_node(ti); const auto foreign_node = find_node(typeid(value_type));
if (!result) { if (!foreign_node) {
// Type was not found // Todo: throw internal error or attach node
// Todo: Throw internal error
return; return;
} }
// Type was found // Type was found
const auto &foreign_node = result.value();
// Check foreign node and relation endpoint // Check foreign node and relation endpoint
if (const auto it = foreign_node->info_->find_relation_endpoint(nodes_.top()->type_index()); it != foreign_node->info().endpoint_end()) { if (const auto it = foreign_node->info_->find_relation_endpoint(nodes_.top()->type_index()); it != foreign_node->info().endpoint_end()) {
// Found corresponding relation endpoint in the foreign node // Found corresponding relation endpoint in the foreign node
if (it->second->is_has_one()) { if (it->second->is_has_one()) {
const auto endpoint = std::make_shared<relation_endpoint>(id, relation_type::BELONGS_TO, foreign_node); const auto endpoint = std::make_shared<relation_endpoint>(id, relation_type::BelongsTo, *foreign_node);
nodes_.top()->info_->register_relation_endpoint(ti, endpoint); nodes_.top()->info_->register_relation_endpoint(ti, endpoint);
link_relation_endpoints(endpoint, it->second); link_relation_endpoints(endpoint, it->second);
} else if (it->second->is_has_many()) { } else if (it->second->is_has_many()) {
const auto endpoint = std::make_shared<relation_endpoint>(id, relation_type::BELONGS_TO, foreign_node); const auto endpoint = std::make_shared<relation_endpoint>(id, relation_type::BelongsTo, *foreign_node);
nodes_.top()->info_->register_relation_endpoint(ti, endpoint); nodes_.top()->info_->register_relation_endpoint(ti, endpoint);
link_relation_endpoints(endpoint, it->second); link_relation_endpoints(endpoint, it->second);
} else if (it->second->foreign_endpoint()->node().type_index() == typeid(many_to_many_relation<Type, value_type>)) { } else if (it->second->foreign_endpoint()->node().type_index() == typeid(many_to_many_relation<Type, value_type>)) {
@@ -359,32 +330,97 @@ void relation_completer<Type>::on_belongs_to(const char *id,
// "belongs_to"-relation the "many_to_many_relation" can be removed // "belongs_to"-relation the "many_to_many_relation" can be removed
// (detach), and the endpoints must be adjusted // (detach), and the endpoints must be adjusted
const auto foreign_endpoint = it->second->foreign_endpoint(); const auto foreign_endpoint = it->second->foreign_endpoint();
const auto detach_result = schema_.detach_node(foreign_endpoint->node_); const auto detach_result = repo_.detach(foreign_endpoint->node_);
foreign_endpoint->node_ = foreign_node; foreign_endpoint->node_ = foreign_node;
// foreign_endpoint->node_ = nodes_.top();
nodes_.top()->info_->register_relation_endpoint(nodes_.top()->type_index(), foreign_endpoint); nodes_.top()->info_->register_relation_endpoint(nodes_.top()->type_index(), foreign_endpoint);
} else { } else {
// check type // check type
} }
} else { } else {
// Relation node was not found, create only endpoint. // Relation node was not found, create only endpoint.
const auto endpoint = std::make_shared<relation_endpoint>(id, relation_type::BELONGS_TO, foreign_node); const auto endpoint = std::make_shared<relation_endpoint>(id, relation_type::BelongsTo, *foreign_node);
nodes_.top()->info_->register_relation_endpoint(ti, endpoint); nodes_.top()->info_->register_relation_endpoint(ti, endpoint);
} }
} }
template<typename Type> template<typename Type, template<typename> typename... Observers>
void relation_completer<Type>::register_relation_endpoints(const endpoint_ptr &endpoint, template<typename NodeType>
void relation_completer<Type, Observers...>::attach_relation_node(const std::string &name, const std::string &join_column, const std::string &inverse_join_column) {
using relation_value_type = many_to_many_relation<Type, NodeType>;
using value_type = NodeType;
// Check if the object_ptr type is already inserted in the schema (by id)
const auto foreign_node = find_node(typeid(value_type));
if (!foreign_node) {
// Todo: throw internal error or attach node
return;
}
if (const auto it = repo_.find_node(name); it != repo_.end()) {
return;
}
// Relation does not exist. Create it.
auto creator = [join_column, inverse_join_column] {
return std::make_unique<relation_value_type>(join_column, inverse_join_column);
};
auto observers = internal::observer_list_copy_creator<Type, relation_value_type, Observers...>::copy_create(observers_);
auto node = repository_node::make_relation_node<relation_value_type>(repo_,
name,
join_column,
inverse_join_column,
std::move(creator),
std::move(observers));
auto result = repo_.attach_node(node.release(), "");
if (!result) {
// Todo: throw internal error
return;
}
auto *attached_node = result.value();
foreign_node_completer<relation_value_type>::complete(result.value(), {});
const auto local_endpoint = std::make_shared<relation_endpoint>(name, relation_type::HasMany, *attached_node);
const auto join_endpoint = std::make_shared<relation_endpoint>(join_column, relation_type::BelongsTo, *nodes_.top());
const auto inverse_join_endpoint = std::make_shared<relation_endpoint>(inverse_join_column, relation_type::BelongsTo, *foreign_node);
const auto foreign_endpoint = std::make_shared<relation_endpoint>(name, relation_type::HasMany, *attached_node);
// register relation endpoint in local node
nodes_.top()->info_->register_relation_endpoint(typeid(value_type), local_endpoint);
// register relation endpoint in foreign node
foreign_node->info_->register_relation_endpoint(nodes_.top()->type_index(), foreign_endpoint);
// register endpoints in relation node
attached_node->info_->register_relation_endpoint(nodes_.top()->type_index(), join_endpoint);
attached_node->info_->register_relation_endpoint(typeid(value_type), inverse_join_endpoint);
// link endpoints
link_relation_endpoints(local_endpoint, join_endpoint);
link_relation_endpoints(foreign_endpoint, inverse_join_endpoint);
}
template<typename Type, template<typename> typename... Observers>
void relation_completer<Type, Observers...>::register_relation_endpoints(const endpoint_ptr &endpoint,
const endpoint_ptr &other_endpoint) { const endpoint_ptr &other_endpoint) {
endpoint->node_->info_->register_relation_endpoint(other_endpoint->node_->type_index(), endpoint); endpoint->node().info_->register_relation_endpoint(other_endpoint->node().type_index(), endpoint);
other_endpoint->node_->info_->register_relation_endpoint(endpoint->node_->type_index(), other_endpoint); other_endpoint->node().info_->register_relation_endpoint(endpoint->node().type_index(), other_endpoint);
link_relation_endpoints(endpoint, other_endpoint); link_relation_endpoints(endpoint, other_endpoint);
} }
template<typename Type> template<typename Type, template<typename> typename... Observers>
void relation_completer<Type>::link_relation_endpoints(const endpoint_ptr &endpoint, const endpoint_ptr &other_endpoint) { void relation_completer<Type, Observers...>::link_relation_endpoints(const endpoint_ptr &endpoint, const endpoint_ptr &other_endpoint) {
endpoint->link_foreign_endpoint(other_endpoint); endpoint->link_foreign_endpoint(other_endpoint);
other_endpoint->link_foreign_endpoint(endpoint); other_endpoint->link_foreign_endpoint(endpoint);
} }
template<typename Type, template <typename> class ... Observers>
repository_node* relation_completer<Type, Observers...>::find_node(const std::type_index &ti) const {
if (const auto it = repo_.find_node(ti); it != repo_.end()) {
return it.get();
}
if (!repo_.is_node_announced(ti)) {
return nullptr;
}
return repo_.announce_node(ti);
}
} }
#endif //RELATION_COMPLETER_HPP #endif //RELATION_COMPLETER_HPP
+14 -14
View File
@@ -8,30 +8,28 @@
namespace matador::object { namespace matador::object {
class schema_node; class repository_node;
enum class relation_type : uint8_t { enum class relation_type : uint8_t {
BELONGS_TO, BelongsTo,
HAS_ONE, HasOne,
HAS_MANY HasMany
}; };
static const utils::enum_mapper<relation_type> relation_type_enum({ static const utils::enum_mapper<relation_type> relation_type_enum({
{ relation_type::BELONGS_TO, "belongs_to" }, { relation_type::BelongsTo, "belongs_to" },
{ relation_type::HAS_ONE, "has_one" }, { relation_type::HasOne, "has_one" },
{ relation_type::HAS_MANY, "has_many" }, { relation_type::HasMany, "has_many" },
}); });
class relation_endpoint { class relation_endpoint {
public: public:
relation_endpoint(std::string field_name, relation_type type, const std::shared_ptr<schema_node>& node); relation_endpoint(std::string field_name, relation_type type, repository_node& node);
[[nodiscard]] std::string field_name() const; [[nodiscard]] std::string field_name() const;
[[nodiscard]] relation_type type() const; [[nodiscard]] relation_type type() const;
[[nodiscard]] std::string type_name() const; [[nodiscard]] std::string type_name() const;
[[nodiscard]] const schema_node& node() const; [[nodiscard]] const repository_node& node() const;
std::shared_ptr<schema_node> node_ptr() const;
[[nodiscard]] bool is_has_one() const; [[nodiscard]] bool is_has_one() const;
[[nodiscard]] bool is_has_many() const; [[nodiscard]] bool is_has_many() const;
@@ -41,13 +39,15 @@ public:
void link_foreign_endpoint(const std::shared_ptr<relation_endpoint>& endpoint); void link_foreign_endpoint(const std::shared_ptr<relation_endpoint>& endpoint);
private: private:
template<typename Type> template<typename Type, template<typename> typename... Observers>
friend class relation_completer; friend class relation_completer;
template<typename Type, template<typename> typename ...Observers>
friend class foreign_node_completer;
std::string field_name_; std::string field_name_;
relation_type type_; relation_type type_;
std::shared_ptr<schema_node> node_; repository_node *node_{nullptr};
std::shared_ptr<relation_endpoint> foreign_endpoint_; std::weak_ptr<relation_endpoint> foreign_endpoint_;
}; };
} }
+93
View File
@@ -0,0 +1,93 @@
#ifndef REPOSITORY_HPP
#define REPOSITORY_HPP
#include "matador/object/basic_repository.hpp"
#include "matador/object/foreign_node_completer.hpp"
#include "matador/object/observer.hpp"
#include "matador/object/relation_completer.hpp"
#include "matador/utils/result.hpp"
#include "matador/utils/error.hpp"
#include <memory>
#include <string>
#include <unordered_set>
namespace matador::object {
class repository : public basic_repository {
public:
using basic_repository::basic_repository;
template<typename Type, template<typename> typename... Observers>
[[nodiscard]] utils::result<void, utils::error> attach(const std::string &name, Observers<Type>&&... observers) {
return attach_type<Type, Observers...>(name, std::string{}, std::forward<Observers<Type>>(observers)...);
}
template<typename Type, typename SuperType, template<typename> typename... Observers>
[[nodiscard]] utils::result<void, utils::error> attach(const std::string &name, Observers<Type>&&... observers) {
const auto ti = std::type_index(typeid(SuperType));
const auto it = find_node(ti);
if (it == end()) {
return utils::failure(make_error(error_code::NodeNotFound, "Parent node '" + std::string(ti.name()) + "' not found"));
}
return attach_type<Type, Observers...>(name, it->name(), std::forward<Observers<Type>>(observers)...);
}
template<typename Type, template<typename> typename... Observers>
[[nodiscard]] utils::result<void, utils::error> attach(const std::string &name, const std::string &parent, Observers<Type>&&... observers) {
return attach_type<Type, Observers...>(name, parent, std::forward<Observers<Type>>(observers)...);
}
template<typename Type, template<typename> typename... Observers>
[[nodiscard]] utils::result<void, utils::error> attach_type(const std::string &name, const std::string &parent, Observers<Type>&&... observers) {
if (const auto it = nodes_by_type_.find(typeid(Type)); it == nodes_by_type_.end() ) {
std::vector<std::unique_ptr<observer<Type>>> obs;
obs.reserve(sizeof...(Observers));
(obs.push_back(std::unique_ptr<observer<Type>>(new Observers<Type>(std::forward<Observers<Type>>(observers)))), ...);
// if the type was not found
std::unique_ptr<repository_node> node;
if (is_node_announced(typeid(Type))) {
node = pop_announce_node(typeid(Type));
node->update_name(name);
} else {
node = repository_node::make_node<Type>(*this, name, []{ return std::make_unique<Type>(); }, std::move(obs));
}
auto result = attach_node(node.release(), parent);
if (!result) {
return utils::failure(result.err());
}
repository_node* attached_node = result.value();
const auto info = attached_node->template info<Type>();
foreign_node_completer<Type, Observers...>::complete(attached_node, info.get().observers());
relation_completer<Type, Observers...>::complete(attached_node, info.get().observers());
} else if (!has_node(name)) {
it->second->update_name(name);
nodes_by_name_[name] = it->second;
if (const auto i = nodes_by_name_.find(""); i != nodes_by_name_.end()) {
nodes_by_name_.erase(i);
}
const auto info = it->second->info<Type>();
relation_completer<Type, Observers...>::complete(it->second, info.get().observers());
log_.info("attach: update node name to '%s' (type: %s)", it->second->name().c_str(), it->second->type_index().name());
} else {
return utils::failure(make_error(error_code::NodeAlreadyExists, "Node '" + name + "' already exists"));
}
return utils::ok<void>();
}
template<typename Type>
[[nodiscard]] utils::result<object_info_ref<Type>, utils::error> info() const {
const auto it = find_node(std::type_index(typeid(Type)));
if (it == end()) {
return utils::failure(make_error(error_code::NodeNotFound, "Parent node '" + std::string(typeid(Type).name()) + "' not found"));
}
return utils::ok(it->info<Type>());
}
};
}
#endif //REPOSITORY_HPP
+135
View File
@@ -0,0 +1,135 @@
#ifndef REPOSITORY_NODE_HPP
#define REPOSITORY_NODE_HPP
#include "matador/object/object_generator.hpp"
#include "matador/object/object_info.hpp"
#include "matador/object/internal/observer_list_creator.hpp"
#include <memory>
namespace matador::object {
class basic_object_info;
class basic_repository;
class repository_node final {
public:
using node_ptr = repository_node*;
template< typename Type>
using creator_func = std::function<std::unique_ptr<Type>()>;
template < typename Type, template<typename> typename... Observers >
static std::unique_ptr<repository_node> make_node(basic_repository& repo,
const std::string& name,
creator_func<Type> creator,
std::vector<std::unique_ptr<observer<Type>>>&& observers);
template < typename Type, template<typename> typename... Observers >
static std::unique_ptr<repository_node> make_relation_node(basic_repository& repo,
const std::string& name,
const std::string& join_column,
const std::string& inverse_join_column,
creator_func<Type> creator,
std::vector<std::unique_ptr<observer<Type>>>&& observers);
explicit repository_node(basic_repository& repo);
repository_node(const repository_node& other) = delete;
repository_node(repository_node&& other) = default;
repository_node& operator=(const repository_node& other) = delete;
repository_node& operator=(repository_node&& other) = delete;
~repository_node() = default;
[[nodiscard]] std::string name() const;
[[nodiscard]] std::type_index type_index() const;
[[nodiscard]] node_ptr next() const;
[[nodiscard]] node_ptr prev() const;
[[nodiscard]] const basic_object_info& info() const;
void update_name(const std::string& name);
template <typename Type>
[[nodiscard]] object_info_ref<Type> info() const {
return std::ref(static_cast<const object_info<Type>&>(*info_));
}
[[nodiscard]] const basic_repository& schema() const;
[[nodiscard]] bool has_children() const;
void on_attach() const;
void on_detach() const;
private:
repository_node(basic_repository& repo, const std::type_index& ti);
repository_node(basic_repository& repo, std::string name, const std::type_index& ti);
void unlink();
private:
friend class basic_repository;
template<typename Type, template<typename> typename... Observers>
friend class relation_completer;
template < typename NodeType, template<typename> typename ...Observers >
friend class foreign_node_completer;
friend class const_repository_node_iterator;
basic_repository &repo_;
std::type_index type_index_;
std::unique_ptr<basic_object_info> info_;
repository_node* parent_{nullptr};
repository_node* previous_sibling_{nullptr};
repository_node* next_sibling_{nullptr};
std::unique_ptr<repository_node> first_child_;
std::unique_ptr<repository_node> last_child_;
std::string name_;
size_t depth_{0};
};
template<typename Type, template <typename> class ... Observers>
std::unique_ptr<repository_node> repository_node::make_node(basic_repository &repo,
const std::string &name,
creator_func<Type> creator,
std::vector<std::unique_ptr<observer<Type>>> &&observers) {
const std::type_index ti(typeid(Type));
auto node = std::unique_ptr<repository_node>(new repository_node(repo, name, ti));
internal::observer_list_creator<Type, Observers...>::create_missing(observers);
auto obj = object_generator::generate<Type>(creator(), repo, name);
node->info_.reset(std::make_unique<object_info<Type>>(
*node,
obj,
std::move(observers),
std::forward<creator_func<Type>>(creator)
).release());
return node;
}
template<typename Type, template <typename> class ... Observers>
std::unique_ptr<repository_node> repository_node::make_relation_node(basic_repository &repo,
const std::string &name,
const std::string &join_column,
const std::string &inverse_join_column,
creator_func<Type> creator,
std::vector<std::unique_ptr<observer<Type>>> &&observers) {
const std::type_index ti(typeid(Type));
auto node = std::unique_ptr<repository_node>(new repository_node(repo, name, ti));
internal::observer_list_creator<Type, Observers...>::create_missing(observers);
auto obj = object_generator::generate<Type>(creator(), repo, name, join_column, inverse_join_column);
node->info_.reset(std::make_unique<object_info<Type>>(
*node,
obj,
std::move(observers),
std::forward<creator_func<Type>>(creator)
).release());
return node;
}
}
#endif //REPOSITORY_NODE_HPP
@@ -1,24 +1,24 @@
#ifndef SCHEMA_NODE_ITERATOR_HPP #ifndef REPOSITORY_NODE_ITERATOR_HPP
#define SCHEMA_NODE_ITERATOR_HPP #define REPOSITORY_NODE_ITERATOR_HPP
#include "matador/object/schema_node.hpp"
#include <iterator> #include <iterator>
namespace matador::object { namespace matador::object {
class const_schema_node_iterator { class repository_node;
class const_repository_node_iterator {
public: public:
using iterator_category = std::bidirectional_iterator_tag; using iterator_category = std::bidirectional_iterator_tag;
using difference_type = std::ptrdiff_t; using difference_type = std::ptrdiff_t;
using value_type = std::shared_ptr<schema_node>; using value_type = repository_node;
using pointer = schema_node*; using pointer = repository_node*;
using reference = value_type; using reference = value_type&;
/** /**
* Creates an empty iterator * Creates an empty iterator
*/ */
const_schema_node_iterator() = default; const_repository_node_iterator() = default;
/** /**
* @brief Creates an iterator for a concrete type. * @brief Creates an iterator for a concrete type.
@@ -28,14 +28,14 @@ public:
* *
* @param node The schema node of the object * @param node The schema node of the object
*/ */
explicit const_schema_node_iterator(const value_type& node); explicit const_repository_node_iterator(value_type* node);
/** /**
* Copy from a given const_object_view_iterator. * Copy from a given const_object_view_iterator.
* *
* @param x The prototype_iterator to copy from. * @param x The prototype_iterator to copy from.
*/ */
const_schema_node_iterator(const const_schema_node_iterator &x) = default; const_repository_node_iterator(const const_repository_node_iterator &x) = default;
/** /**
* Assign from a given prototype_iterator. * Assign from a given prototype_iterator.
@@ -43,59 +43,59 @@ public:
* @param x The prototype_iterator to assign from. * @param x The prototype_iterator to assign from.
* @return The assigned prototype_iterator. * @return The assigned prototype_iterator.
*/ */
const_schema_node_iterator& operator=(const const_schema_node_iterator &x) = default; const_repository_node_iterator& operator=(const const_repository_node_iterator &x) = default;
~const_schema_node_iterator() = default; ~const_repository_node_iterator() = default;
/** /**
* @brief Compares this with another iterators. * @brief Compares this with other iterators.
* *
* Compares this with another iterators. Returns true * Compares this with other iterators. Returns true
* if the iterators node prototype_type are the same. * if the iterators node prototype_type are the same.
* *
* @param i The iterator to compare with. * @param i The iterator to compare with.
* @return True if the iterators are the same. * @return True if the iterators are the same.
*/ */
bool operator==(const const_schema_node_iterator &i) const; bool operator==(const const_repository_node_iterator &i) const;
/** /**
* @brief Compares this with another iterators. * @brief Compares this with other iterators.
* *
* Compares this with another iterators. Returns true * Compares this with other iterators. Returns true
* if the iterators node prototype_node are not the same. * if the iterators node prototype_node are different.
* *
* @param i The iterator to compare with. * @param i The iterator to compare with.
* @return True if the iterators are not the same. * @return True if the iterators are different.
*/ */
bool operator!=(const const_schema_node_iterator &i) const; bool operator!=(const const_repository_node_iterator &i) const;
/** /**
* Pre increments the iterator * Pre-increments the iterator
* *
* @return Returns iterators successor. * @return Returns iterators successor.
*/ */
const_schema_node_iterator& operator++(); const_repository_node_iterator& operator++();
/** /**
* Post increments the iterator * Post increments the iterator
* *
* @return Returns iterator before incrementing. * @return Returns iterator before incrementing.
*/ */
const_schema_node_iterator operator++( int ); const_repository_node_iterator operator++( int );
/** /**
* Pre increments the iterator * Pre-increments the iterator
* *
* @return Returns iterators predecessor. * @return Returns iterators predecessor.
*/ */
const_schema_node_iterator& operator--(); const_repository_node_iterator& operator--();
/** /**
* Post decrements the iterator * Post decrements the iterator
* *
* @return Returns iterator before decrementing. * @return Returns iterator before decrementing.
*/ */
const_schema_node_iterator operator--(int); const_repository_node_iterator operator--(int);
/** /**
* Returns the pointer to the node. * Returns the pointer to the node.
@@ -123,8 +123,8 @@ private:
void decrement(); void decrement();
private: private:
value_type node_; pointer node_{nullptr};
}; };
} }
#endif //SCHEMA_NODE_ITERATOR_HPP #endif //REPOSITORY_NODE_ITERATOR_HPP
+46
View File
@@ -0,0 +1,46 @@
#ifndef MATADOR_RESTRICTION_HPP
#define MATADOR_RESTRICTION_HPP
#include "matador/utils/constraints.hpp"
#include <memory>
#include <ostream>
#include <string>
#include <variant>
namespace matador::object {
class attribute;
class constraint_builder;
class constraint_generator;
class object;
class restriction {
public:
explicit restriction(size_t attr_index);
[[nodiscard]] size_t attribute_index() const;
[[nodiscard]] std::string column_name() const;
[[nodiscard]] utils::constraints options() const;
[[nodiscard]] std::shared_ptr<object> owner() const;
[[nodiscard]] bool is_primary_key_constraint() const;
[[nodiscard]] bool is_foreign_key_constraint() const;
[[nodiscard]] bool is_unique_constraint() const;
[[nodiscard]] std::string ref_table_name() const;
[[nodiscard]] std::string ref_column_name() const;
friend std::ostream& operator<<(std::ostream& os, const restriction& c);
[[nodiscard]] std::string type_string() const;
private:
friend class constraint_builder;
friend class object_generator;
friend class object;
const size_t index_;
std::weak_ptr<object> owner_;
std::weak_ptr<object> reference_;
utils::constraints options_{utils::constraints::None};
};
}
#endif //MATADOR_RESTRICTION_HPP
-190
View File
@@ -1,190 +0,0 @@
#ifndef SCHEMA_HPP
#define SCHEMA_HPP
#include "matador/logger/log_manager.hpp"
#include "matador/object/error_code.hpp"
#include "matador/object/foreign_node_completer.hpp"
#include "matador/object/relation_completer.hpp"
#include "matador/object/schema_node.hpp"
#include "matador/object/schema_node_iterator.hpp"
#include "matador/utils/result.hpp"
#include "matador/utils/error.hpp"
#include "matador/logger/logger.hpp"
#include <memory>
#include <string>
#include <unordered_set>
namespace matador::object {
namespace internal {
class shadow_schema;
}
utils::error make_error(error_code ec, const std::string &msg);
class schema {
public:
typedef const_schema_node_iterator const_iterator; /**< Shortcut for the list const iterator. */
using node_ptr = std::shared_ptr<schema_node>;
/**
* Creates an empty schema
*/
explicit schema(std::string name = "");
template<typename Type>
[[nodiscard]] utils::result<void, utils::error> attach(const std::string &name, const std::string &parent = "") {
if (const auto it = nodes_by_type_.find(typeid(Type)); it == nodes_by_type_.end() ) {
// if the type was not found
auto node = schema_node::make_node<Type>(*this, name);
if (auto result = attach_node(node, parent); !result) {
return utils::failure(result.err());
}
foreign_node_completer::complete<Type>(node);
relation_completer<Type>::complete(node);
} else if (!has_node(name)) {
it->second->update_name(name);
nodes_by_name_[name] = it->second;
relation_completer<Type>::complete(it->second);
log_.info("attach: update node name to '%s' (type: %s)", it->second->name().c_str(), it->second->type_index().name());
} else {
// remove_node( )
return utils::failure(make_error(error_code::NodeAlreadyExists, "Node '" + name + "' already exists"));
}
return utils::ok<void>();
}
template<typename Type, typename SuperType>
[[nodiscard]] utils::result<void, utils::error> attach(const std::string &name) {
const auto ti = std::type_index(typeid(SuperType));
auto result = find_node(ti);
if (!result) {
return utils::failure(make_error(error_code::NodeNotFound, "Parent node '" + std::string(ti.name()) + "' not found"));
}
return attach<Type>(name, (*result)->name());
}
/**
* Detaches a given node from the schema. If the
* node is a parent of other nodes, these nodes are
* detached as well.
*
* @param node Node to detach from schema
* @return Result object indicating success or failure
*/
[[nodiscard]] utils::result<void, utils::error> detach(const node_ptr &node);
/**
* Return the first schema node.
*
* @return The first schema node iterator.
*/
[[nodiscard]] const_iterator begin() const;
/**
* Return the last schema node.
*
* @return The last schema node iterator.
*/
[[nodiscard]] const_iterator end() const;
/**
* Returns true if the schema contains
* no schema nodes.
*
* @return True if the schema is empty
*/
[[nodiscard]] bool empty() const;
/**
* Returns the current number of the schema node.
*
* @return Number of schema nodes
*/
[[nodiscard]] size_t size() const;
/**
* Returns the name of the schema.
*
* @return The name of the schema
*/
[[nodiscard]] std::string name() const;
[[nodiscard]] bool contains(const std::string &name) const;
[[nodiscard]] bool contains(const std::type_index &index) const;
template < typename Type >
[[nodiscard]] bool contains() const {
return contains(std::type_index(typeid(Type)));
}
template<typename Type>
[[nodiscard]] utils::result<object_info_ref<Type>, utils::error> info() const {
auto result = find_node(std::type_index(typeid(Type)));
if (!result) {
return utils::failure(result.err());
}
return utils::ok(result.value()->info<Type>());
}
template<typename Type>
[[nodiscard]] utils::result<basic_object_info_ref, utils::error> basic_info() const {
auto result = find_node(std::type_index(typeid(Type)));
if (!result) {
return utils::failure(result.err());
}
return utils::ok(basic_object_info_ref{result.value()->info()});
}
[[nodiscard]] utils::result<std::shared_ptr<attribute_definition>, utils::error> reference_column(
const std::type_index &type_index) const;
void dump(std::ostream &os) const;
static void dump(std::ostream &os, const node_ptr& node);
private:
using t_node_map = std::unordered_map<std::string, node_ptr>;
using t_type_index_node_map = std::unordered_map<std::type_index, node_ptr>;
[[nodiscard]] utils::result<node_ptr, utils::error> attach_node(const node_ptr &node, const std::string &parent);
// [[nodiscard]] utils::result<node_ptr, utils::error> attach_node(const node_ptr &node, const std::type_index &type_index);
[[nodiscard]] utils::result<node_ptr, utils::error> find_node(const std::string &name) const;
[[nodiscard]] utils::result<node_ptr, utils::error> find_node(const std::type_index &type_index) const;
template<typename Type>
[[nodiscard]] utils::result<node_ptr, utils::error> find_node() const {
return find_node(std::type_index(typeid(Type)));
}
[[nodiscard]] bool has_node(const std::string &name) const;
[[nodiscard]] bool has_node(const std::type_index &index) const;
[[nodiscard]] bool has_node(const node_ptr& node) const;
static void insert_node(const node_ptr &parent, const node_ptr &child);
void remove_node(const node_ptr &node);
private:
friend class internal::shadow_schema;
friend class schema_node;
friend class attribute_definition_generator;
std::string name_;
std::shared_ptr<schema_node> root_;
t_node_map nodes_by_name_;
t_type_index_node_map nodes_by_type_;
logger::logger log_;
std::unordered_map<std::type_index, std::shared_ptr<attribute_definition> > missing_references_;
};
}
#endif //SCHEMA_HPP
-118
View File
@@ -1,118 +0,0 @@
#ifndef SCHEMA_NODE_HPP
#define SCHEMA_NODE_HPP
#include "matador/object/attribute_definition_generator.hpp"
#include "matador/object/object_info.hpp"
#include "matador/object/primary_key_resolver.hpp"
#include <memory>
namespace matador::object {
class basic_object_info;
class schema;
class schema_node final {
public:
using node_ptr = std::shared_ptr<schema_node>;
template < typename Type >
static std::shared_ptr<schema_node> make_node(object::schema& tree, const std::string& name) {
auto node = std::shared_ptr<schema_node>(new schema_node(tree, name, typeid(Type)));
primary_key_resolver resolver;
auto pk_info = resolver.resolve<Type>();
auto ref_column = determine_reference_column(typeid(Type), name, pk_info, tree);
auto info = std::make_unique<object_info<Type>>(
node,
std::move(pk_info.pk),
ref_column,
object_definition{attribute_definition_generator::generate<Type>(tree)},
[]{ return std::make_unique<Type>(); }
);
node->info_ = std::move(info);
return node;
}
template < typename Type, typename CreatorFunc >
static utils::result<node_ptr, utils::error> make_relation_node(object::schema& tree, const std::string& name, CreatorFunc &&creator) {
const auto result = make_and_attach_node(tree, name, typeid(Type));
if (!result) {
return result;
}
auto obj = creator();
auto info = std::make_unique<object_info<Type>>(
result.value(),
object_definition{attribute_definition_generator::generate(*obj, tree)},
std::move(creator)
);
result.value()->info_ = std::move(info);
return result;
}
static std::shared_ptr<schema_node> make_null_node(schema& tree);
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;
[[nodiscard]] node_ptr next() const;
[[nodiscard]] node_ptr prev() const;
[[nodiscard]] const basic_object_info& info() const;
void update_name(const std::string& name);
template <typename Type>
object_info_ref<Type> info() const {
return std::ref(static_cast<const object_info<Type>&>(*info_));
}
[[nodiscard]] const object::schema& schema() const;
[[nodiscard]] bool has_children() const;
private:
explicit schema_node(object::schema& tree);
schema_node(object::schema& tree, const std::type_index& ti);
schema_node(object::schema& tree, std::string name, const std::type_index& ti);
void unlink();
static utils::result<node_ptr, utils::error> make_and_attach_node(object::schema& tree, const std::string& name, const std::type_index& ti);
static std::shared_ptr<attribute_definition> determine_reference_column(const std::type_index& ti,
const std::string& name,
const primary_key_info& pk_info,
object::schema& tree);
private:
friend class schema;
template<typename Type>
friend class relation_completer;
friend class foreign_node_completer;
friend class const_schema_node_iterator;
object::schema &schema_;
std::type_index type_index_;
std::unique_ptr<basic_object_info> info_;
std::shared_ptr<schema_node> parent_;
std::shared_ptr<schema_node> previous_sibling_;
std::shared_ptr<schema_node> next_sibling_;
std::shared_ptr<schema_node> first_child_;
std::shared_ptr<schema_node> last_child_;
std::string name_;
size_t depth_{0};
};
}
#endif //SCHEMA_NODE_HPP
-33
View File
@@ -1,33 +0,0 @@
#ifndef ERROR_CODE_HPP
#define ERROR_CODE_HPP
#include <cstdint>
#include <system_error>
namespace matador::orm {
enum class error_code : uint8_t {
Ok = 0,
NoConnectionAvailable,
UnknownType,
FailedToBuildQuery,
FailedToFindObject
};
class orm_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& orm_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<matador::orm::error_code> : true_type {};
#endif //ERROR_CODE_HPP
@@ -1,29 +0,0 @@
#ifndef QUERY_BUILDER_EXCEPTION_HPP
#define QUERY_BUILDER_EXCEPTION_HPP
#include <cstdint>
#include <system_error>
namespace matador::orm {
enum class query_build_error : std::uint8_t {
Ok = 0,
UnknownType,
MissingPrimaryKey,
UnexpectedError
};
class query_builder_exception final : public std::exception {
public:
explicit query_builder_exception(const query_build_error error) : error_(error) {}
[[nodiscard]] query_build_error error() const { return error_; }
private:
const query_build_error error_;
};
}
#endif //QUERY_BUILDER_EXCEPTION_HPP
-180
View File
@@ -1,180 +0,0 @@
#ifndef QUERY_SESSION_HPP
#define QUERY_SESSION_HPP
#include "matador/orm/error_code.hpp"
#include "matador/orm/session_query_builder.hpp"
#include "matador/query/query.hpp"
#include "matador/sql/column_generator.hpp"
#include "matador/sql/connection.hpp"
#include "matador/sql/connection_pool.hpp"
#include "matador/sql/statement.hpp"
#include "matador/object/object_ptr.hpp"
#include "matador/object/object_definition.hpp"
#include "matador/object/schema.hpp"
#include <unordered_map>
namespace matador::orm {
utils::error make_error(error_code ec, const std::string &msg);
class session final {
public:
explicit session(sql::connection_pool &pool);
template<typename Type>
[[nodiscard]] utils::result<void, utils::error> attach(const std::string &table_name) const;
template<typename Type, typename SuperType>
[[nodiscard]] utils::result<void, utils::error> attach(const std::string &table_name) const;
utils::result<void, utils::error> create_schema() const;
template<typename Type>
utils::result<object::object_ptr<Type>, utils::error> insert(Type *obj);
template< class Type, typename... Args >
utils::result<object::object_ptr<Type>, utils::error> insert(Args&&... args) {
return insert(new Type(std::forward<Args>(args)...));
}
template<typename Type, typename PrimaryKeyType>
utils::result<object::object_ptr<Type>, utils::error> find(const PrimaryKeyType &pk) {
auto c = pool_.acquire();
if (!c.valid()) {
return utils::failure(make_error(error_code::NoConnectionAvailable, "Failed to acquire connection."));
}
auto info = schema_->info<Type>();
if (!info) {
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
}
session_query_builder eqb(*schema_);
auto data = eqb.build<Type>(pk);
if (!data.is_ok()) {
return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build query for type " + info->get().name() + "."));
}
auto obj = build_select_query(data.release()).template fetch_one<Type>(*c);
if (!obj) {
return utils::failure(obj.err());
}
if (!obj->get()) {
return utils::failure(make_error(error_code::FailedToFindObject, "Failed to find object of type " + info->get().name() + " with primary key " + std::to_string(pk) + "."));
}
return utils::ok(object::object_ptr<Type>{ obj->release() });
}
template<typename Type>
utils::result<sql::query_result<Type>, utils::error> find() {
auto c = pool_.acquire();
if (!c.valid()) {
return utils::failure(make_error(error_code::NoConnectionAvailable, "Failed to acquire connection."));
}
auto info = schema_->info<Type>();
if (!info) {
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
}
session_query_builder eqb(*schema_);
auto data = eqb.build<Type>();
if (!data.is_ok()) {
return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build query for type " + info->get().name() + "."));
}
return build_select_query(data.release()).template fetch_all<Type>(*c);
}
template<typename Type>
utils::result<query::query_from_intermediate, utils::error> select() {
auto c = pool_.acquire();
if (!c.valid()) {
return utils::failure(make_error(error_code::NoConnectionAvailable, "Failed to acquire connection."));
}
auto info = schema_->info<Type>();
if (!info) {
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
}
session_query_builder eqb(*schema_);
auto data = eqb.build<Type>();
if (!data.is_ok()) {
return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build query for type " + info->get().name() + "."));
}
return utils::ok(build_select_query(data.release()).template fetch_all<Type>(*c));
}
template<typename Type>
utils::result<void, utils::error> drop_table();
utils::result<void, utils::error> drop_table(const std::string &table_name) const;
[[nodiscard]] utils::result<sql::query_result<sql::record>, utils::error> fetch(const sql::query_context &q) const;
[[nodiscard]] size_t execute(const std::string &sql) const;
[[nodiscard]] sql::statement prepare(const sql::query_context& q) const;
[[nodiscard]] std::vector<object::attribute_definition> describe_table(const std::string &table_name) const;
[[nodiscard]] bool table_exists(const std::string &table_name) const;
[[nodiscard]] const sql::dialect& dialect() const;
void dump_schema(std::ostream &os) const;
private:
friend class query_select;
static query::fetchable_query build_select_query(entity_query_data &&data);
private:
sql::connection_pool &pool_;
const sql::dialect &dialect_;
std::unique_ptr<object::schema> schema_;
mutable std::unordered_map<std::string, object::object_definition> prototypes_;
};
template<typename Type>
[[nodiscard]] utils::result<void, utils::error> session::attach(const std::string &table_name) const {
return schema_->attach<Type>(table_name);
}
template<typename Type, typename SuperType>
utils::result<void, utils::error> session::attach( const std::string& table_name ) const {
return schema_->attach<Type, SuperType>(table_name);
}
template<typename Type>
utils::result<object::object_ptr<Type>, utils::error> session::insert(Type *obj)
{
auto c = pool_.acquire();
auto info = schema_->info<Type>();
if (!info) {
return utils::failure(info.err());
}
auto res = query::query::insert()
.into(info->get().name(), sql::column_generator::generate<Type>(*schema_, true))
.values(*obj)
.execute(*c);
if (!res) {
return utils::failure(res.err());
}
return utils::ok(object::object_ptr{obj});
}
template<typename Type>
utils::result<void, utils::error> session::drop_table() {
auto info = schema_->info<Type>();
if (info) {
return drop_table(info->get().name());
}
return utils::failure(info.err());
}
}
#endif //QUERY_SESSION_HPP
@@ -1,131 +0,0 @@
#ifndef SESSION_INSERT_BUILDER_HPP
#define SESSION_INSERT_BUILDER_HPP
#include "matador/orm/query_builder_exception.hpp"
#include "matador/query/condition.hpp"
#include "matador/query/query_intermediates.hpp"
#include "matador/object/schema.hpp"
#include "matador/utils/cascade_type.hpp"
#include "matador/utils/primary_key_attribute.hpp"
namespace matador::orm {
struct entity_insert_data {
sql::table table;
std::vector<std::string> columns;
std::vector<utils::database_type> values;
};
enum class insert_build_error : std::uint8_t {
Ok = 0,
UnknownType,
MissingPrimaryKey,
UnexpectedError
};
class insert_builder_exception final : public std::exception {
public:
explicit insert_builder_exception(const insert_build_error error) : error_(error) {
}
[[nodiscard]] insert_build_error error() const { return error_; }
private:
const insert_build_error error_;
};
class session_insert_builder final {
public:
explicit session_insert_builder(const object::schema &scm)
: schema_(scm) {
}
template<class EntityType>
utils::result<std::vector<entity_insert_data>, insert_build_error> build(const EntityType &obj) {
auto info = schema_.info<EntityType>();
if (!info) {
return utils::failure(insert_build_error::UnknownType);
}
table_info_stack_.push({info.value()});
entity_insert_data_ = {{sql::table{info.value().get().name()}}};
// processed_tables_.insert({info->get().name(), entity_insert_data_.root_table});
try {
access::process(*this, obj);
return {utils::ok(std::move(entity_insert_data_))};
} catch (const insert_builder_exception &ex) {
return {utils::failure(ex.error())};
} catch (...) {
return {utils::failure(insert_build_error::UnexpectedError)};
}
}
template<class V>
void on_primary_key(const char *id, V &x, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {
push(id, x);
}
void on_revision(const char *id, unsigned long long &/*rev*/);
template<typename Type>
void on_attribute(const char *id, Type &x, const utils::field_attributes &/*attr*/ = utils::null_attributes) {
push(id, x);
}
template<class Pointer>
void on_belongs_to(const char *id, Pointer &obj, const utils::foreign_attributes &attr) {
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::INSERT)) {
return;
}
const auto info = schema_.info<typename Pointer::value_type::value_type>();
if (!info) {
throw query_builder_exception{query_build_error::UnknownType};
}
}
template<class Pointer>
void on_has_one(const char *id, Pointer &obj, const utils::foreign_attributes &attr) {
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::INSERT)) {
return;
}
const auto info = schema_.info<typename Pointer::value_type::value_type>();
if (!info) {
throw query_builder_exception{query_build_error::UnknownType};
}
}
template<class ContainerType>
void on_has_many(const char * /*id*/, ContainerType &, const char *join_column,
const utils::foreign_attributes &attr) {
}
template<class ContainerType>
void on_has_many_to_many(const char *id, ContainerType &/*cont*/, const char *join_column,
const char *inverse_join_column, const utils::foreign_attributes &attr) {
}
template<class ContainerType>
void on_has_many_to_many(const char *id, ContainerType &/*cont*/, const utils::foreign_attributes &attr) {
}
private:
void push(const std::string &column_name, const utils::database_type &value);
private:
struct table_info {
std::reference_wrapper<const object::basic_object_info> info;
};
std::stack<table_info> table_info_stack_;
std::unordered_map<std::string, std::shared_ptr<sql::table> > processed_tables_;
const object::schema &schema_;
std::vector<entity_insert_data> entity_insert_data_;
};
}
#endif //SESSION_INSERT_BUILDER_HPP
@@ -1,287 +0,0 @@
#ifndef QUERY_ENTITY_QUERY_BUILDER_HPP
#define QUERY_ENTITY_QUERY_BUILDER_HPP
#include "matador/orm/query_builder_exception.hpp"
#include "matador/query/condition.hpp"
#include "matador/query/query_intermediates.hpp"
#include "matador/sql/connection.hpp"
#include "matador/object/join_columns_collector.hpp"
#include "matador/object/schema.hpp"
#include "matador/utils/primary_key_attribute.hpp"
#include "matador/utils/result.hpp"
#include "matador/utils/value.hpp"
#include <stack>
#include <unordered_set>
namespace matador::orm {
struct entity_query_data {
std::shared_ptr<sql::table> root_table;
std::string pk_column_name{};
std::vector<sql::column> columns{};
std::vector<query::join_data> joins{};
std::unique_ptr<query::basic_condition> where_clause{};
};
class session_query_builder final
{
public:
explicit session_query_builder(const object::schema &scm)
: schema_(scm) {}
template<class EntityType, typename PrimaryKeyType>
utils::result<entity_query_data, query_build_error> build(const PrimaryKeyType &pk) {
auto info = schema_.info<EntityType>();
if (!info) {
return utils::failure(query_build_error::UnknownType);
}
pk_ = pk;
table_info_stack_.push({info.value(), std::make_shared<sql::table>(info.value().get().name(), build_alias('t', ++table_index))});
entity_query_data_ = { table_info_stack_.top().table };
processed_tables_.insert({info->get().name(), entity_query_data_.root_table});
try {
access::process(*this, info->get().prototype());
return {utils::ok(std::move(entity_query_data_))};
} catch (const query_builder_exception &ex) {
return {utils::failure(ex.error())};
} catch (...) {
return {utils::failure(query_build_error::UnexpectedError)};
}
}
template<class EntityType>
utils::result<entity_query_data, query_build_error> build() {
const auto info = schema_.info<EntityType>();
if (!info) {
return utils::failure(query_build_error::UnknownType);
}
pk_ = nullptr;
table_info_stack_.push({info.value(), std::make_shared<sql::table>(info.value().get().name(), build_alias('t', ++table_index))});
entity_query_data_ = { table_info_stack_.top().table };
processed_tables_.insert({info->get().name(), entity_query_data_.root_table});
try {
access::process(*this, info->get().prototype());
return {utils::ok(std::move(entity_query_data_))};
} catch (const query_builder_exception &ex) {
return {utils::failure(ex.error())};
} catch (...) {
return {utils::failure(query_build_error::UnexpectedError)};
}
}
template < class V >
void on_primary_key(const char *id, V &, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes)
{
push(id);
if (!is_root_entity()) {
return;
}
if (pk_.is_null()) {
entity_query_data_.pk_column_name = id;
} else if (pk_.is_integer()) {
auto v = *pk_.as<V>();
auto c = sql::column{table_info_stack_.top().table, id, ""};
auto co = std::make_unique<query::condition<sql::column, V>>(c, query::basic_condition::operand_type::EQUAL, v);
entity_query_data_.where_clause = std::move(co);
entity_query_data_.pk_column_name = id;
} else if (pk_.is_varchar()) {
// Todo: handle varchar primary key
}
}
void on_revision(const char *id, unsigned long long &/*rev*/);
template<typename Type>
void on_attribute(const char *id, Type &, const utils::field_attributes &/*attr*/ = utils::null_attributes)
{
push(id);
}
template<class Pointer>
void on_belongs_to(const char *id, Pointer &obj, const utils::foreign_attributes &attr)
{
on_foreign_object(id, obj, attr);
}
template<class Pointer>
void on_has_one(const char *id, Pointer &obj, const utils::foreign_attributes &attr)
{
on_foreign_object(id, obj, attr);
}
template<class ContainerType>
void on_has_many(const char * /*id*/, ContainerType &, const char *join_column, const utils::foreign_attributes &attr) {
if (attr.fetch() == utils::fetch_type::EAGER) {
const auto info = schema_.info<typename ContainerType::value_type::value_type>();
if (!info) {
throw query_builder_exception{query_build_error::UnknownType};
}
auto next = processed_tables_.find(info->get().name());
if (next != processed_tables_.end()) {
return;
}
table_info_stack_.push({info.value(), std::make_shared<sql::table>(info->get().name(), build_alias('t', ++table_index))});
next = processed_tables_.insert({info->get().name(), table_info_stack_.top().table}).first;
typename ContainerType::value_type::value_type obj;
access::process(*this , obj);
table_info_stack_.pop();
auto pk = info->get().definition().primary_key();
if (!pk) {
throw query_builder_exception{query_build_error::MissingPrimaryKey};
}
append_join(
sql::column{table_info_stack_.top().table, table_info_stack_.top().info.get().definition().primary_key()->name()},
sql::column{next->second, join_column}
);
}
}
template<class ContainerType>
void on_has_many_to_many(const char *id, ContainerType &/*cont*/, 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<typename ContainerType::value_type::value_type>();
if (!info) {
throw query_builder_exception{query_build_error::UnknownType};
}
auto next = processed_tables_.find(info->get().name());
if (next != processed_tables_.end()) {
return;
}
auto relation = processed_tables_.find(id);
if (relation == processed_tables_.end()) {
relation = processed_tables_.insert({id, std::make_shared<sql::table>(id, build_alias('t', ++table_index))}).first;
}
table_info_stack_.push({info.value(), std::make_shared<sql::table>(info->get().name(), build_alias('t', ++table_index))});
next = processed_tables_.insert({info->get().name(), table_info_stack_.top().table}).first;
typename ContainerType::value_type::value_type obj;
access::process(*this , obj);
table_info_stack_.pop();
auto pk = info->get().definition().primary_key();
if (!pk) {
throw query_builder_exception{query_build_error::MissingPrimaryKey};
}
append_join(
sql::column{table_info_stack_.top().table, table_info_stack_.top().info.get().definition().primary_key()->name()},
sql::column{relation->second, join_column}
);
append_join(
sql::column{relation->second, inverse_join_column},
sql::column{next->second, pk->name()}
);
}
template<class ContainerType>
void on_has_many_to_many(const char *id, ContainerType &/*cont*/, const utils::foreign_attributes &attr) {
if (attr.fetch() != utils::fetch_type::EAGER) {
return;
}
const auto info = schema_.info<typename ContainerType::value_type::value_type>();
if (!info) {
throw query_builder_exception{query_build_error::UnknownType};
}
auto next = processed_tables_.find(info->get().name());
if (next != processed_tables_.end()) {
return;
}
auto relation = processed_tables_.find(id);
if (relation == processed_tables_.end()) {
relation = processed_tables_.insert({id, std::make_shared<sql::table>(id, build_alias('t', ++table_index))}).first;
}
table_info_stack_.push({info.value(), std::make_shared<sql::table>(info->get().name(), build_alias('t', ++table_index))});
next = processed_tables_.insert({info->get().name(), table_info_stack_.top().table}).first;
typename ContainerType::value_type::value_type obj;
access::process(*this , obj);
table_info_stack_.pop();
auto pk = info->get().definition().primary_key();
if (!pk) {
throw query_builder_exception{query_build_error::MissingPrimaryKey};
}
const auto join_columns = join_columns_collector_.collect<typename ContainerType::value_type::value_type>();
append_join(
sql::column{table_info_stack_.top().table, table_info_stack_.top().info.get().definition().primary_key()->name()},
sql::column{relation->second, join_columns.inverse_join_column}
);
append_join(
sql::column{relation->second, join_columns.join_column},
sql::column{next->second, pk->name()}
);
}
private:
template<class Pointer>
void on_foreign_object(const char *id, Pointer &, const utils::foreign_attributes &attr);
void push(const std::string &column_name);
static std::string build_alias(char prefix, unsigned int count);
[[nodiscard]] bool is_root_entity() const;
void append_join(const sql::column &left, const sql::column &right);
private:
utils::value pk_;
struct table_info {
std::reference_wrapper<const object::basic_object_info> info;
std::shared_ptr<sql::table> table;
};
std::stack<table_info> table_info_stack_;
std::unordered_map<std::string, std::shared_ptr<sql::table>> processed_tables_;
const object::schema &schema_;
entity_query_data entity_query_data_;
unsigned int column_index{0};
unsigned int table_index{0};
object::join_columns_collector join_columns_collector_;
};
template<class Pointer>
void session_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<typename Pointer::value_type>();
if (!info) {
throw query_builder_exception{query_build_error::UnknownType};
}
auto next = processed_tables_.find(info->get().name());
if (next != processed_tables_.end()) {
return;
}
table_info_stack_.push({info.value(), std::make_shared<sql::table>(info->get().name(), build_alias('t', ++table_index))});
next = processed_tables_.insert({info->get().name(), table_info_stack_.top().table}).first;
typename Pointer::value_type obj;
access::process(*this, obj);
table_info_stack_.pop();
auto pk = info->get().definition().primary_key();
if (!pk) {
throw query_builder_exception{query_build_error::MissingPrimaryKey};
}
append_join(
sql::column{table_info_stack_.top().table, id},
sql::column{next->second, pk->name()}
);
} else {
push(id);
}
}
}
#endif //QUERY_ENTITY_QUERY_BUILDER_HPP
@@ -0,0 +1,29 @@
#ifndef MATADOR_ABSTRACT_PK_GENERATOR_HPP
#define MATADOR_ABSTRACT_PK_GENERATOR_HPP
#include "matador/utils/error.hpp"
#include "matador/utils/result.hpp"
#include "matador/utils/primary_key_generator_type.hpp"
#include <cstdint>
namespace matador::sql {
class executor;
}
namespace matador::query {
class abstract_pk_generator {
public:
virtual ~abstract_pk_generator() = default;
virtual utils::result<int64_t, utils::error> next_id(const sql::executor& exec) = 0;
virtual utils::result<int64_t, utils::error> current_id(const sql::executor& exec) = 0;
[[nodiscard]] utils::generator_type type() const;
protected:
explicit abstract_pk_generator(utils::generator_type type);
private:
utils::generator_type generator_type_{};
};
}
#endif //MATADOR_ABSTRACT_PK_GENERATOR_HPP
@@ -3,17 +3,13 @@
#include "matador/utils/attribute_writer.hpp" #include "matador/utils/attribute_writer.hpp"
#include <optional> #include "matador/sql/interface/connection_impl.hpp"
namespace matador::sql { #include <optional>
class dialect;
class connection_impl;
}
namespace matador::query { namespace matador::query {
class attribute_string_writer final : public utils::attribute_writer class attribute_string_writer final : public utils::attribute_writer {
{
public: public:
attribute_string_writer(const sql::dialect &d, std::optional<std::reference_wrapper<const sql::connection_impl>> conn); attribute_string_writer(const sql::dialect &d, std::optional<std::reference_wrapper<const sql::connection_impl>> conn);
@@ -38,13 +34,14 @@ public:
void write_value(size_t pos, const bool& 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 float& x) override;
void write_value(size_t pos, const double& 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 utils::date_type_t& x) override;
void write_value(size_t pos, const date& x) override; void write_value(size_t pos, const utils::time_type_t& x) override;
void write_value(size_t pos, const utils::timestamp_type_t& x) override;
void write_value(size_t pos, const char* 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 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) override;
void write_value(size_t pos, const std::string& x, size_t size) 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::blob_type_t& x) override;
void write_value(size_t pos, const utils::value& x, size_t size) override; void write_value(size_t pos, const utils::value& x, size_t size) override;
private: private:
-66
View File
@@ -1,66 +0,0 @@
#ifndef QUERY_BASIC_CONDITION_HPP
#define QUERY_BASIC_CONDITION_HPP
#include "matador/sql/column.hpp"
#include <unordered_map>
#include <cstdint>
#include <string>
namespace matador::sql {
class dialect;
struct query_context;
}
namespace matador::query {
class basic_condition
{
public:
basic_condition() = default;
virtual ~basic_condition() = default;
enum class operand_type : uint8_t
{
EQUAL = 0,
NOT_EQUAL,
LESS,
LESS_EQUAL,
GREATER,
GREATER_EQUAL,
OR,
AND,
NOT,
IN_LIST,
LIKE
};
virtual std::string evaluate(const sql::dialect &dialect, sql::query_context &query) const = 0;
static std::unordered_map<operand_type, std::string> operands;
};
class basic_column_condition : public basic_condition
{
public:
sql::column field_;
std::string operand;
basic_column_condition(sql::column fld, operand_type op);
};
class basic_in_condition : public basic_condition
{
public:
sql::column field_;
explicit basic_in_condition(sql::column fld);
[[nodiscard]] virtual size_t size() const = 0;
};
/// @endcond
}
#endif //QUERY_BASIC_CONDITION_HPP
+120
View File
@@ -0,0 +1,120 @@
#ifndef MATADOR_BASIC_SCHEMA_HPP
#define MATADOR_BASIC_SCHEMA_HPP
#include "matador/object/repository.hpp"
#include "matador/query/abstract_pk_generator.hpp"
#include "matador/query/table.hpp"
#include "matador/sql/internal/joined_collection_resolver_producer.hpp"
#include "matador/sql/internal/object_resolver_producer.hpp"
#include "matador/sql/producer_object_resolver_factory.hpp"
#include <memory>
#include <typeindex>
#include <unordered_map>
namespace matador::sql {
class connection;
}
namespace matador::query {
class schema_node final {
public:
schema_node(class table tab, std::unique_ptr<abstract_pk_generator> &&pk_generator, const object::repository_node& node);
[[nodiscard]] const std::string& name() const;
[[nodiscard]] const class table& table() const;
[[nodiscard]] abstract_pk_generator& pk_generator() const;
[[nodiscard]] const object::repository_node& node() const;
private:
class table table_;
std::unique_ptr<abstract_pk_generator> pk_generator_;
const object::repository_node& node_;
};
class primary_key_generator_finder final {
public:
template< typename Type >
utils::generator_type find(const object::object_info<Type>& info) {
generator_type_ = utils::generator_type::Manual;
access::process(*this, info.prototype());
return generator_type_;
}
template<typename BaseType>
static void on_base(const BaseType&) {}
template<class V>
void on_primary_key(const char * /*id*/, V &/*pk*/, const utils::primary_key_attribute &attr) {
generator_type_ = attr.generator();
}
static void on_revision(const char * /*id*/, uint64_t & /*rev*/) {}
template<typename T>
static void on_attribute(const char * /*id*/, T &, const utils::field_attributes &/*attr*/) {}
template<class P>
static void on_belongs_to(const char * /*id*/, P &, const utils::foreign_attributes & ) {}
template<class P>
static void on_has_one(const char * /*id*/, P &, const char * /*join_column*/, const utils::foreign_attributes & ) {}
template<class C>
static void on_has_many(const char * /*id*/, C &, const char * /*join_column*/, const utils::foreign_attributes & ) {}
template<class C>
static void on_has_many_to_many(const char * /*id*/, C &, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes & ) {}
template<class C>
static void on_has_many_to_many(const char * /*id*/, C &, const utils::foreign_attributes & ) {}
private:
utils::generator_type generator_type_{};
};
class basic_schema {
public:
using schema_node_map = std::unordered_map<std::type_index, schema_node>;
using iterator = std::unordered_map<std::type_index, schema_node>::iterator;
using const_iterator = std::unordered_map<std::type_index, schema_node>::const_iterator;
basic_schema();
explicit basic_schema(const std::string &name);
[[nodiscard]] std::string name() const;
[[nodiscard]] const object::repository &repo() const;
object::repository &repo();
[[nodiscard]] utils::result<void, utils::error> create(const sql::connection &conn) const;
[[nodiscard]] utils::result<void, utils::error> drop(const sql::connection &conn) const;
iterator begin();
iterator end();
[[nodiscard]] const_iterator begin() const;
[[nodiscard]] const_iterator end() const;
[[nodiscard]] size_t size() const;
[[nodiscard]] bool empty() const;
iterator find(const std::type_index& ti);
iterator find(const std::string& name);
[[nodiscard]] const_iterator find(const std::type_index& ti) const;
[[nodiscard]] const_iterator find(const std::string& name) const;
[[nodiscard]] bool contains(const std::type_index &index) const;
[[nodiscard]] const std::unordered_map<std::type_index, std::unique_ptr<sql::object_resolver_producer>>& resolver_producers() const;
[[nodiscard]] const std::unordered_map<object::collection_composite_key, std::unique_ptr<sql::joined_collection_resolver_producer>, object::collection_composite_key_hash>& collection_resolver_producers() const;
[[nodiscard]] const std::unordered_map<object::collection_composite_key, std::unique_ptr<sql::joined_object_resolver_producer>, object::collection_composite_key_hash>& joined_object_resolver_producers() const;
protected:
template<typename Type>
friend class schema_observer;
friend class producer_creator;
object::repository repo_;
std::unordered_map<std::type_index, schema_node> schema_nodes_;
std::unordered_map<std::type_index, std::unique_ptr<sql::object_resolver_producer>> resolver_producers_;
std::unordered_map<object::collection_composite_key, std::unique_ptr<sql::joined_collection_resolver_producer>, object::collection_composite_key_hash> collection_resolver_producers_;
std::unordered_map<object::collection_composite_key, std::unique_ptr<sql::joined_object_resolver_producer>, object::collection_composite_key_hash> joined_object_resolver_producers_;
};
}
#endif //MATADOR_BASIC_SCHEMA_HPP
+121
View File
@@ -0,0 +1,121 @@
#ifndef MATADOR_BUILDER_HPP
#define MATADOR_BUILDER_HPP
#include "matador/query/table_column.hpp"
#include "matador/query/table_constraint.hpp"
#include "matador/query/table.hpp"
#include "matador/utils/basic_types.hpp"
#include "matador/utils/constraints.hpp"
#include "matador/utils/data_type_traits.hpp"
#include "matador/utils/types.hpp"
#include <string>
namespace matador::object {
class data_type {
public:
explicit data_type(const utils::basic_type type, const size_t size = 0)
: type_(type), size_(size) {
}
[[nodiscard]] const utils::basic_type &type() const { return type_; }
[[nodiscard]] size_t size() const { return size_; }
private:
utils::basic_type type_{};
size_t size_{0};
};
template<typename Type>
class typed_data_type final : public data_type {
public:
typed_data_type()
: data_type(utils::data_type_traits<Type>::type()) {
}
};
template<typename Type>
class sized_typed_data_type final : public data_type {
public:
explicit sized_typed_data_type(size_t size)
: data_type(utils::data_type_traits<Type>::type(size), size) {
}
};
using TinyInt = typed_data_type<int8_t>;
using SmallInt = typed_data_type<int16_t>;
using Integer = typed_data_type<int32_t>;
using BigInt = typed_data_type<int64_t>;
using TinyIntUnsigned = typed_data_type<uint8_t>;
using SmallIntUnsigned = typed_data_type<uint16_t>;
using IntegerUnsigned = typed_data_type<uint32_t>;
using BigIntUnsigned = typed_data_type<uint64_t>;
using Real = typed_data_type<float>;
using Double = typed_data_type<double>;
using Text = typed_data_type<std::string>;
using Boolean = typed_data_type<bool>;
using Varchar = sized_typed_data_type<std::string>;
using Blob = sized_typed_data_type<std::vector<std::byte> >;
using Time = typed_data_type<utils::time_type_t>;
using Date = typed_data_type<utils::date_type_t>;
using Timestamp = typed_data_type<utils::timestamp_type_t>;
}
namespace matador::query {
class column_builder {
public:
explicit column_builder(std::string column_name, utils::basic_type type, size_t size = 0);
// ReSharper disable once CppNonExplicitConversionOperator
operator table_column() const; // NOLINT(*-explicit-constructor)
column_builder &not_null();
column_builder &primary_key();
column_builder &unique();
column_builder &identity();
private:
std::string column_name_;
utils::basic_type type_{};
utils::constraints constraints_{utils::constraints::NotNull};
size_t size_{0};
};
class table_builder {
public:
explicit table_builder(std::string name);
// ReSharper disable once CppNonExplicitConversionOperator
operator table() const; // NOLINT(*-explicit-constructor)
private:
std::string table_name;
};
class constraint_builder {
public:
constraint_builder &constraint(std::string name);
constraint_builder &primary_key(std::string name);
constraint_builder &foreign_key(std::string name);
constraint_builder &references(std::string table, std::string column);
// ReSharper disable once CppNonExplicitConversionOperator
operator table_constraint() const; // NOLINT(*-explicit-constructor)
private:
std::string constraint_name_;
std::string column_name_;
std::string ref_table_name_;
std::string ref_column_name_;
utils::constraints type_{};
};
constraint_builder constraint(std::string name);
// table_builder table(std::string name);
column_builder column(std::string name, utils::basic_type type, size_t size = 0);
}
#endif //MATADOR_BUILDER_HPP
-635
View File
@@ -1,635 +0,0 @@
#ifndef QUERY_CONDITION_HPP
#define QUERY_CONDITION_HPP
#include "matador/query/basic_condition.hpp"
#include "matador/sql/dialect.hpp"
#include "matador/sql/query_context.hpp"
#include "matador/utils/placeholder.hpp"
#include <memory>
#include <utility>
namespace matador::query {
/**
* @class condition
* @brief Represents a sql query condition
*
* @tparam L Left hand operator type
* @tparam R Right hand operator type
* This class represents a condition part
* of a sql query or update statement.
* Each compare method returns a reference to
* the condition itself. That way one can
* concatenate conditions together.
*/
/// @cond MATADOR_DEV
template<class L, class R, class Enabled = void>
class condition;
template<>
class condition<sql::column, utils::placeholder, std::enable_if_t<true>> final : public basic_column_condition
{
public:
condition(const sql::column &fld, operand_type op, const utils::placeholder &val);
utils::placeholder value;
std::string evaluate(const sql::dialect &d, sql::query_context &query) const override;
};
template<class T>
class condition<sql::column, T, std::enable_if_t<
std::is_scalar_v<T> &&
!std::is_same_v<std::string, T> &&
!std::is_same_v<const char*, T>>> final : public basic_column_condition
{
public:
condition(const sql::column &fld, const operand_type op, T val)
: basic_column_condition(fld, op)
, value(val)
{ }
T value;
std::string evaluate(const sql::dialect &d, sql::query_context &query) const override
{
query.bind_vars.emplace_back(field_.name);
return d.prepare_condition(field_) + " " + operand + " " + std::to_string(value);
}
};
template<class T>
class condition<sql::column, T, std::enable_if_t<
std::is_same_v<std::string, T> ||
std::is_same_v<const char*, T>>>final : public basic_column_condition
{
public:
condition(const sql::column &fld, const operand_type op, T val)
: basic_column_condition(fld, op)
,value(val)
{ }
T value;
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 + "'";
}
};
template<class T>
class condition<T, sql::column, std::enable_if_t<
std::is_scalar_v<T> &&
!std::is_same_v<std::string, T> &&
!std::is_same_v<const char*, T>>> final : public basic_column_condition
{
public:
condition(T val, const operand_type op, const sql::column &fld)
: basic_column_condition(fld, op)
, value(val)
{ }
T value;
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 T>
class condition<T, sql::column, std::enable_if_t<
std::is_same_v<std::string, T> ||
std::is_same_v<const char*, T>>> final : public basic_column_condition
{
public:
condition(T val, const operand_type op, const sql::column &fld)
: basic_column_condition(fld, op)
, value(val)
{ }
T value;
std::string evaluate(const sql::dialect &d, sql::query_context &query) const override
{
return "'" + std::to_string(value) + "' " + operand + " " + d.prepare_identifier(field_);
}
};
/// @endcond
/**
* @brief Condition class representing an IN condition
*
* This class represents an query IN condition and evaluates to
* this condition based on the current database d
*
* @code
* WHERE age IN (29,34,56)
* @endcode
*/
template < class V >
class condition<sql::column, std::initializer_list<V>> final : public basic_in_condition {
public:
/**
* @brief Creates an IN condition
*
* 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 sql::column &col, const std::initializer_list<V> &args)
: basic_in_condition(col), args_(args) {}
/**
* @brief Evaluates the condition
*
* Evaluates the condition to a part of the
* 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 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);
}
std::string result = d.prepare_identifier(field_) + " IN (";
if (args_.size() < 2) {
for (const auto &val : args_) {
result.append(std::to_string(val));
}
} else {
auto it = args_.begin();
result.append(std::to_string(*it++));
for(; it != args_.end(); ++it) {
result.append(", " + std::to_string(*it));
}
}
result += ")";
return result;
}
/**
* @brief Returns the number of arguments in the list
* @return The number of arguments in the list
*/
[[nodiscard]] size_t size() const override {
return args_.size();
}
private:
std::vector<V> args_;
};
/**
* @brief Condition class representing an IN condition
*
* 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 table)
* @endcode
*/
template <>
class condition<sql::column, sql::query_context> final : public basic_column_condition
{
public:
/**
* @brief Create a query IN condition
*
* Create an IN condition where the argument values come from
* the given query. To evaluate the query a sql d must be
* given.
*
* @param col Column for the IN condition
* @param op Operand of the condition
* @param q The query to be evaluated to the IN arguments
*/
condition(sql::column col, operand_type op, sql::query_context &q);
/**
* @brief Evaluates the condition
*
* Evaluates the condition to a part of the
* 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 sql::dialect &d, sql::query_context &query) const override;
private:
sql::query_context &query_;
};
/**
* @brief Between condition.
*
* The condition represents a between where clause
* part.
*
* @tparam T The type of the boundary values
*/
template < class T >
class condition<sql::column, std::pair<T, T>> final : public basic_condition {
public:
/**
* @brief Create a new between condition
*
* @param col The column for the range check
* @param range The boundary values defining the range
*/
condition(sql::column col, const std::pair<T, T> &range)
: field_(std::move(col)), range_(range) {}
/**
* @brief Evaluates the condition
*
* Evaluates the condition to a between part
* 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 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:
sql::column field_;
std::pair<T, T> range_;
};
/**
* @brief Logical binary condition
*
* This class represents a logical binary condition
* - AND
* - OR
*
* @tparam L1 The left hand type of the left operator
* @tparam R1 The right hand type of the left operator
* @tparam L2 The left hand type of the right operator
* @tparam R2 The right hand type of the right operator
*/
template<class L1, class R1, class L2, class R2>
class condition<condition<L1, R1>, condition<L2, R2>> final : public basic_condition
{
public:
/**
* @brief Create a binary logical condition
* @param l Left hand operator of the condition
* @param r right hand operator of the condition
* @param op The operand (AND or OR)
*/
condition(condition<L1, R1> &&l, condition<L2, R2> &&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 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 == operand_type::AND) {
return "(" + cl + " " + operands[operand] + " " + cr + ")";
} else {
return cl + " " + operands[operand] + " " + cr;
}
}
private:
condition<L1, R1> left;
condition<L2, R2> right;
basic_condition::operand_type operand;
};
/**
* @brief Logical unary condition
*
* This class represents a logical unary NOT condition
*
* @tparam L Left hand type of the condition to be negated
* @tparam R Right hand type of the condition to be negated
*/
template<class L, class R>
class condition<condition<L, R>, void> final : public basic_condition
{
public:
/**
* @brief Create a logical unary condition
* @param c The condition to be negated
*/
condition(const condition<L, R> &c) // NOLINT(*-explicit-constructor)
: 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 sql::dialect &d, sql::query_context &query) const override
{
return operand + " (" + cond.evaluate(d, query) + ")";
}
private:
condition<L, R> cond;
std::string operand;
};
template<>
class condition<sql::column, sql::column> final : public basic_column_condition
{
public:
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 sql::dialect &d, sql::query_context &query) const override;
private:
sql::column other_column_;
};
/**
* @file condition.hpp
* @brief Contains functions to create query conditions
*
* This file contains some functions to create
* query conditions. These functions wrap the
* constructing of a concrete condition and allows
* expression like condition programming.
*
* @code
* cond1 == cond2
* cond1 < cond2
* cond2 != cond1 && cond3 < cond4
* @endcode
*/
/**
* @brief Creates an IN condition for a given column and a list of values
*
* @tparam V The type of the list arguments
* @param col The column to compare
* @param args The list of values
* @return The condition object
*/
template < class V >
condition<sql::column, std::initializer_list<V>> in(const sql::column &col, std::initializer_list<V> args)
{
return condition<sql::column, std::initializer_list<V>>(col, args);
}
/**
* @brief Creates an IN condition for a given column and a query to be executed
*
* @param col The column to compare
* @param q The query to be executes as sub select
* @return The condition object
*/
condition<sql::column, sql::query_context> in(const sql::column &col, sql::query_context &&q);
/**
* @brief Creates a between condition.
*
* Creates a between condition for the given column with
* the given range consisting of a low and high value
*
* @tparam T The type of the column and range
* @param col The column for the between condition
* @param low The low value of the range
* @param high The high value of the range
* @return The condition object
*/
template<class T>
condition<sql::column, std::pair<T, T>> between(const sql::column &col, T low, T high)
{
return condition<sql::column, std::pair<T, T>>(col, std::make_pair(low, high));
}
/**
* @brief Creates a like condition
*
* Creates a like condition for the given column
* and the given value string.
*
* @param col The column for the like condition
* @param val The value to the like operator
* @return The like condition object
*/
condition<sql::column, std::string> like(const sql::column &col, const std::string &val);
/**
* @brief Condition equality operator for a column and a value
*
* Creates a condition object of a column and a value
* checked on equality.
*
* @tparam T The type of the value
* @param col The column object
* @param val The value to compare with
* @return The condition object representing the equality operation
*/
template<class T>
condition<sql::column, T> operator==(const sql::column &col, T val)
{
return condition<sql::column, T>(col, basic_condition::operand_type::EQUAL, val);
}
condition<sql::column, sql::column> operator==(const sql::column &a, const sql::column &b);
/**
* @brief Condition equality method for a column and a query
*
* Creates a condition object of a column and a query
* checked on equality.
*
* @param col The column object
* @param q The query to compare with
* @return The condition object representing the equality operation
*/
condition<sql::column, sql::query_context> equals(const sql::column &col, sql::query_context &q);
/**
* @brief Condition inequality operator for 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
* @param col The column object
* @param val The value to compare with
* @return The condition object representing the inequality operation
*/
template<class T>
condition<sql::column, T> operator!=(const sql::column &col, T val)
{
return condition<sql::column, T>(col, basic_condition::operand_type::NOT_EQUAL, val);
}
/**
* @brief Condition less operator for a column and a value
*
* Creates a condition object checking if the value of the given column
* is less than the given value.
*
* @tparam T The type of the value
* @param col The column object
* @param val The value to compare with
* @return The condition object representing the less operation
*/
template<class T>
condition<sql::column, T> operator<(const sql::column &col, T val)
{
return condition<sql::column, T>(col, basic_condition::operand_type::LESS, val);
}
/**
* @brief Condition less or equal operator for a column and a value
*
* Creates a condition object checking if the value of the given column
* is less or equal than the given value.
*
* @tparam T The type of the value
* @param col The column object
* @param val The value to compare with
* @return The condition object representing the less or equal operation
*/
template<class T>
condition<sql::column, T> operator<=(const sql::column &col, T val)
{
return condition<sql::column, T>(col, basic_condition::operand_type::LESS_EQUAL, val);
}
/**
* @brief Condition greater operator for a column and a value
*
* Creates a condition object checking if the value of the given column
* is greater than the given value.
*
* @tparam T The type of the value
* @param col The column object
* @param val The value to compare with
* @return The condition object representing the greater operation
*/
template<class T>
condition<sql::column, T> operator>(const sql::column &col, T val)
{
return condition<sql::column, T>(col, basic_condition::operand_type::GREATER, val);
}
/**
* @brief Condition greater or equal operator for a column and a value
*
* Creates a condition object checking if the value of the given column
* is greater or equal than the given value.
*
* @tparam T The type of the value
* @param col The column object
* @param val The value to compare with
* @return The condition object representing the greater or equal operation
*/
template<class T>
condition<sql::column, T> operator>=(const sql::column &col, T val)
{
return condition<sql::column, T>(col, basic_condition::operand_type::GREATER_EQUAL, val);
}
/**
* @brief AND operation condition consisting of a left and right hand condition
*
* @tparam L1 Left hand type of left hand condition
* @tparam R1 Right hand type of left hand condition
* @tparam L2 Left hand type of right hand condition
* @tparam R2 Right hand type of right hand condition
* @param l Left hand condition
* @param r Right hand condition
* @return An condition object representing the AND operation
*/
template<class L1, class R1, class L2, class R2>
condition<condition<L1, R1>, condition<L2, R2>> operator&&(condition<L1, R1> l, condition<L2, R2> r)
{
return condition<condition<L1, R1>, condition<L2, R2>>(std::move(l), std::move(r), basic_condition::operand_type::AND);
}
/**
* @brief OR operation condition consisting of a left and right hand condition
*
* @tparam L1 Left hand type of left hand condition
* @tparam R1 Right hand type of left hand condition
* @tparam L2 Left hand type of right hand condition
* @tparam R2 Right hand type of right hand condition
* @param l Left hand condition
* @param r Right hand condition
* @return An condition object representing the OR operation
*/
template<class L1, class R1, class L2, class R2>
condition<condition<L1, R1>, condition<L2, R2>> operator||(condition<L1, R1> l, condition<L2, R2> r)
{
return condition<condition<L1, R1>, condition<L2, R2>>(std::move(l), std::move(r), basic_condition::operand_type::OR);
}
/**
* @brief Negates a given condition.
*
* @tparam L The left hand type of the condition
* @tparam R The right hand type of the condition
* @param c The condition to negated
* @return An condition object representing the NOT operation
*/
template<class L, class R>
condition<condition<L, R>, void> operator!(condition<L, R> c)
{
return condition<condition<L, R>, void>(std::move(c));
}
/**
* @brief Creates a unique condition from a given condition object
*
* @tparam L The left hand type of the condition
* @tparam R The right hand type of the condition
* @param cond The condition to be copied
* @return A unique condition pointer representing the given condition
*/
template<class L, class R>
std::unique_ptr<basic_condition> make_condition(const condition<L, R> &cond)
{
return std::make_unique<condition<L, R>>(cond);
}
}
#endif //QUERY_CONDITION_HPP
+14
View File
@@ -0,0 +1,14 @@
#ifndef CRITERIA_CRITERIA_HPP
#define CRITERIA_CRITERIA_HPP
#include "matador/query/criteria/between_criteria.hpp"
#include "matador/query/criteria/binary_criteria.hpp"
#include "matador/query/criteria/check_null_criteria.hpp"
#include "matador/query/criteria/collection_criteria.hpp"
#include "matador/query/criteria/like_criteria.hpp"
#include "matador/query/criteria/logical_criteria.hpp"
#include "matador/query/criteria/not_criteria.hpp"
#include "matador/query/criteria/criteria_operators.hpp"
#endif //CRITERIA_CRITERIA_HPP
@@ -0,0 +1,22 @@
#ifndef MATADOR_ABSTRACT_COLUMN_CRITERIA_HPP
#define MATADOR_ABSTRACT_COLUMN_CRITERIA_HPP
#include "matador/query/criteria/abstract_criteria.hpp"
#include "matador/query/table_column.hpp"
namespace matador::query {
class abstract_column_criteria : public abstract_criteria {
public:
abstract_column_criteria() = delete;
explicit abstract_column_criteria(const table_column& col);
[[nodiscard]] const table_column& col() const;
protected:
table_column column_;
};
}
#endif //MATADOR_ABSTRACT_COLUMN_CRITERIA_HPP
@@ -0,0 +1,20 @@
#ifndef CRITERIA_CRITERIA_NODE_HPP
#define CRITERIA_CRITERIA_NODE_HPP
#include <memory>
namespace matador::query {
class criteria_visitor;
class abstract_criteria {
public:
virtual ~abstract_criteria() = default;
virtual void accept(criteria_visitor& visitor) const = 0;
};
using criteria_ptr = std::unique_ptr<abstract_criteria>;
}
#endif //CRITERIA_CRITERIA_NODE_HPP
@@ -0,0 +1,25 @@
#ifndef CRITERIA_BETWEEN_CRITERIA_NODE_HPP
#define CRITERIA_BETWEEN_CRITERIA_NODE_HPP
#include "matador/query/criteria/abstract_column_criteria.hpp"
#include "matador/query/criteria/criteria_utils.hpp"
namespace matador::query {
class between_criteria final : public abstract_column_criteria {
public:
between_criteria() = delete;
between_criteria(const table_column& col, int64_t min, int64_t max);
between_criteria(const table_column& col, utils::placeholder min, utils::placeholder max);
void accept(criteria_visitor& visitor) const override;
[[nodiscard]] const criteria_value &minimum() const;
[[nodiscard]] const criteria_value &maximum() const;
private:
criteria_value min_;
criteria_value max_;
};
}
#endif //CRITERIA_BETWEEN_CRITERIA_NODE_HPP

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