ENH: Implement queries and entity retrieval
Summary
See https://gitlab.indiscale.com/caosdb/customers/lfpb/management/-/issues/386.
Focus
Point the reviewer to the core of the code change. Where should they start
reading? What should they focus on (e.g. security, performance,
maintainability, user-friendliness, compliance with the specs, finding more
corner cases, concrete questions)?
Test Environment
How to set up a test environment for manual testing?
Check List for the Author
Please, prepare your MR for a review. Be sure to write a summary and a focus and create gitlab comments for the reviewer. They should guide the reviewer through the changes, explain your changes and also point out open questions. For further good practices have a look at our review guidelines
-
All automated tests pass -
Reference related Issues -
Up-to-date CHANGELOG.md -
Annotations in code (Gitlab comments) - Intent of new code
- Problems with old code
- Why this implementation?
Check List for the Reviewer
-
I understand the intent of this MR -
All automated tests pass -
Up-to-date CHANGELOG.md -
The test environment setup works and the intended behavior is reproducible in the test environment -
In-code documentation and comments are up-to-date. -
Check: Are there spezifications? Are they satisfied?
For further good practices have a look at our review guidelines.
Merge request reports
Activity
mentioned in merge request caosdb-juliainttest!3 (merged)
added 8 commits
- d7537e93 - MAINT: Add caosdb_client.json for tests
- c01a8864 - DOC: Add hint to config file
- 1cf223b6 - DRAFT: Move Transaction module to separate file
- 82d75b25 - DRAFT: Begin tests for transactions
- 3daa6ee5 - ENH: Implement create_transaction
- e40a8c77 - MAINT: Add caosdb config file
- 381fff84 - ENH: Implement retrievals and update docstrings
- a1ce9bdd - STY: whitespaces
Toggle commit list5 5 is meant for expert use only. Only use those if you know what you're 6 6 doing. 7 7 8 ```@index 9 Order = [:module, :function, :macro, :type, :constant] - src/Transaction.jl 0 → 100644
118 Add a sub-request to retrieve several entities by their `ids` to the 119 given `transaction`. 120 121 !!! info 122 123 This does not execute the transaction. 124 """ 125 function add_retrieve_by_id( 126 transaction::Ref{_Transaction}, 127 ids::Vector{T}, 128 ) where {T<:AbstractString} 129 130 err_code = ccall( 131 (:caosdb_transaction_transaction_retrieve_by_ids, CaosDB.library_name), 132 Cint, 133 (Ref{_Transaction}, Ptr{Ptr{Cchar}}), changed this line in version 4 of the diff
added 6 commits
- 45dabd84 - DRAFT: Begin implementation of entities
- 5ef97a84 - ENH: Add Exception type for client exceptions
- 475a378f - FIX: Correct arguments for retrieve_by_ids
- 2b379aff - DRAFT: Begin implementation of entities
- f5bd5529 - ENH: Add test for entities
- 0be053a7 - DRAFT: Add a lot of setters, getters, and helper functions
Toggle commit listadded 1 commit
- b3e4ab79 - MAINT: Move remaining modules to separate files
- src/Authentication.jl 0 → 100644
57 end 58 59 Base.showerror(io::IO, e::CaosDBException) = 60 print(io, "CaosDBException: ", e.msg, " (Status code ", e.code, ")") 61 62 """ 63 Struct containing Messages and codes for status codes<0 that do not 64 correspond to errors or success. 65 """ 66 struct CaosDBMessage 67 msg::String 68 code::Cint 69 end 70 71 Base.show(io::IO, m::CaosDBMessage) = print(io, m.msg, " (Status code ", m.code, ")") 102 # include modules from other files 34 34 """ 35 35 mutable struct _Connection 36 36 wrapped_connection::Ptr{Cvoid} 37 _deletable::Bool 34 34 """ 35 35 mutable struct _Connection 36 36 wrapped_connection::Ptr{Cvoid} 37 _deletable::Bool 37 38 38 39 function _Connection(managed_by_julia::Bool = false) 39 40 conn = new() 41 conn._deletable = false - src/Exceptions.jl 0 → 100644
- src/Exceptions.jl 0 → 100644
36 37 """ 38 A generic exception that will be raised in case of non-zero return 39 values of the calls to libccaosdb. May carry a message string and a 40 code. 41 """ 42 struct GenericCaosDBException <: CaosDBException 43 msg::String 44 code::Cint 45 end 46 47 """ 48 Something went wrong on the client-side or the user is attempting to 49 conduct an invalid operation. 50 """ 51 struct ClientException <: CaosDBException - src/Exceptions.jl 0 → 100644
42 struct GenericCaosDBException <: CaosDBException 43 msg::String 44 code::Cint 45 end 46 47 """ 48 Something went wrong on the client-side or the user is attempting to 49 conduct an invalid operation. 50 """ 51 struct ClientException <: CaosDBException 52 msg::String 53 code::Cint 54 55 function ClientException(message::AbstractString) 56 client_error_code = 57 ccall((:caosdb_status_code_OTHER_CLIENT_ERROR, CaosDB.library_name), Cint, ()) - src/Info.jl 0 → 100644
- src/Transaction.jl 0 → 100644
61 end 62 return trans 63 end 64 end 65 66 """ 67 Struct containing a pointer to the wrapped cpp result set of a 68 transaction. The struct is meant for internal use only and shouldn't 69 be created directly by the user but is returned by, e.g., 70 `get_result_set`. 71 """ 72 mutable struct _ResultSet 73 wrapped_result_set::Ptr{Cvoid} 74 _deletable::Bool 75 76 function _ResultSet() - src/Transaction.jl 0 → 100644
74 _deletable::Bool 75 76 function _ResultSet() 77 rs = new() 78 rs._deletable = false 79 return rs 80 end 81 end 82 83 """ 84 create_transaction(name::AbstractString = "default") 85 86 Return a transaction created with the connection of the given name. If 87 none is given, the defult connection is used. 88 """ 89 function create_transaction(name::AbstractString = "default") Overload the creator s.th. users only have to type
create_connection()
and get one for the default connection which is probably what the users would want in most cases. They can also call this with the string name of the connection they want to use, or with the connection object itself if they already have created one.
- src/Transaction.jl 0 → 100644
87 none is given, the defult connection is used. 88 """ 89 function create_transaction(name::AbstractString = "default") 90 connection = CaosDB.Connection.get_connection(name) 91 92 return create_transaction(connection) 93 end 94 95 """ 96 function create_transaction(connection::Ref{CaosDB.Connection._Connection}) 97 98 Return a transactioncreated with the given connection object. 99 """ 100 function create_transaction(connection::Ref{CaosDB.Connection._Connection}) 101 102 transaction = Ref{_Transaction}(_Transaction(true)) - src/Transaction.jl 0 → 100644
112 CaosDB.Exceptions.evaluate_return_code(err_code) 113 114 return transaction 115 end 116 117 """ 118 function add_retrieve_by_id(transaction::Ref{_Transaction}, id::AbstractString) 119 120 Add a sub-request to retrieve a single entity by its `id` to the given 121 `transaction`. 122 123 !!! info 124 125 This does not execute the transaction. 126 """ 127 function add_retrieve_by_id(transaction::Ref{_Transaction}, id::AbstractString) - src/Transaction.jl 0 → 100644
141 function add_retrieve_by_id( 142 transaction::Ref{_Transaction}, 143 ids::Vector{T}, 144 ) where {T<:AbstractString} 145 146 Add a sub-request to retrieve several entities by their `ids` to the 147 given `transaction`. 148 149 !!! info 150 151 This does not execute the transaction. 152 """ 153 function add_retrieve_by_id( 154 transaction::Ref{_Transaction}, 155 ids::Vector{T}, 156 ) where {T<:AbstractString} - src/Transaction.jl 0 → 100644
147 given `transaction`. 148 149 !!! info 150 151 This does not execute the transaction. 152 """ 153 function add_retrieve_by_id( 154 transaction::Ref{_Transaction}, 155 ids::Vector{T}, 156 ) where {T<:AbstractString} 157 158 len = Cint(length(ids)) 159 err_code = ccall( 160 (:caosdb_transaction_transaction_retrieve_by_ids, CaosDB.library_name), 161 Cint, 162 (Ref{_Transaction}, Ptr{Ptr{Cchar}}, Cint), - src/Transaction.jl 0 → 100644
188 err_code = ccall( 189 (:caosdb_transaction_transaction_query, CaosDB.library_name), 190 Cint, 191 (Ref{_Transaction}, Cstring), 192 transaction, 193 query, 194 ) 195 196 CaosDB.Exceptions.evaluate_return_code(err_code) 197 end 198 199 """ 200 function execute(transaction::Ref{_Transaction}) 201 202 Send the given `transaction` to the CaosDB server for excecution and 203 wait for it to finish. - src/Transaction.jl 0 → 100644
207 err_code = ccall( 208 (:caosdb_transaction_transaction_execute, CaosDB.library_name), 209 Cint, 210 (Ref{_Transaction},), 211 transaction, 212 ) 213 214 CaosDB.Exceptions.evaluate_return_code(err_code) 215 end 216 217 """ 218 function get_result_set(transaction::Ref{_Transaction}) 219 220 Return the result set of the given `transaction`. 221 """ 222 function get_result_set(transaction::Ref{_Transaction}) - src/Transaction.jl 0 → 100644
338 339 Return al results of the given `result_set`. 340 """ 341 function get_results(result_set::Ref{_ResultSet}) 342 343 size = get_result_size(result_set) 344 345 return [get_result_at(result_set, Cint(ii)) for ii = 1:size] 346 end 347 348 """ 349 function get_results(transaction::Ref{_Transaction}) 350 351 Return all results of the given `transaction`. 352 """ 353 function get_results(transaction::Ref{_Transaction}) - src/Transaction.jl 0 → 100644
363 name::AbstractString = "default" 364 ) 365 366 Execute the given `query` and return its results. Use the connection 367 with the given `name`. If none is given, the default connection is 368 used. 369 370 !!! info 371 372 Since only the resulting entities are returned, this only makes 373 sense for FIND (and, in the future, SELECT) queries. To get the 374 result of a `COUNT` query, you have to execute the transaction 375 yourself using `create_transaction`, `add_query`, and `execute`, 376 and get the result with `get_count_result`. 377 """ 378 function execute_query(query::AbstractString, name::AbstractString = "default") Convenience functions further simplifying queries and retrievals: The users don't have to care about connections (though they may still specify them by name or connection object if needed), transactions, adding subrequests, and getting the results after execution; they simply type
execute_query()
orretrieve()
and are returned the entities directly.
- src/Transaction.jl 0 → 100644
394 395 Execute the given `query` and return its results. Use the given 396 `connection`. 397 398 !!! info 399 400 Since only the resulting entities are returned, this only makes 401 sense for FIND (and, in the future, SELECT) queries. To get the 402 result of a `COUNT` query, you have to execute the transaction 403 yourself using `create_transaction`, `add_query`, and `execute`, 404 and get the result with `get_count_result`. 405 """ 406 function execute_query( 407 query::AbstractString, 408 connection::Ref{CaosDB.Connection._Connection}, 409 ) - src/Transaction.jl 0 → 100644
412 413 add_query(transaction, query) 414 415 execute(transaction) 416 417 return get_results(transaction) 418 end 419 420 """ 421 function retrieve(id::AbstractString, name::AbstractString = "default") 422 423 Retrieve and return the entity with the given `id`. Use the connection 424 with the given `name`. If none is provided, the default connection is 425 used. 426 """ 427 function retrieve(id::AbstractString, name::AbstractString = "default") - src/Utility.jl 0 → 100644
63 63 Cint(-1), 64 64 ), 65 65 ) CaosDB.Exceptions.evaluate_return_code(Cint(-1)) 66 67 # Check whether client errors can be built correctly and 68 # return the correct code (Change it if this ever changes in 69 # libcaosdb) 70 client_err = CaosDB.Exceptions.ClientException("Client error") 71 @test client_err.msg == "Client error" 72 @test client_err.code == 9999 75 @testset "TestTransaction" begin 76 # transactions can be generated from the default connection, 77 # from a connection name, or from a connection object 78 @test CaosDB.Transaction.create_transaction() != nothing 79 @test CaosDB.Transaction.create_transaction("default") != nothing 80 conn = CaosDB.Connection.get_connection() 81 @test CaosDB.Transaction.create_transaction(conn) != nothing 82 83 # Retrieval works by a single id, by a list of ids, or by querying 84 trans1 = CaosDB.Transaction.create_transaction() 85 @test CaosDB.Transaction.add_retrieve_by_id(trans1, "some_id") == nothing 86 trans2 = CaosDB.Transaction.create_transaction() 87 @test CaosDB.Transaction.add_retrieve_by_id(trans2, ["id1", "id2", "id3"]) == 88 nothing 89 trans3 = CaosDB.Transaction.create_transaction() 90 @test CaosDB.Transaction.add_query(trans3, "FIND ENTITY WITH id=123") == nothing 80 conn = CaosDB.Connection.get_connection() 81 @test CaosDB.Transaction.create_transaction(conn) != nothing 82 83 # Retrieval works by a single id, by a list of ids, or by querying 84 trans1 = CaosDB.Transaction.create_transaction() 85 @test CaosDB.Transaction.add_retrieve_by_id(trans1, "some_id") == nothing 86 trans2 = CaosDB.Transaction.create_transaction() 87 @test CaosDB.Transaction.add_retrieve_by_id(trans2, ["id1", "id2", "id3"]) == 88 nothing 89 trans3 = CaosDB.Transaction.create_transaction() 90 @test CaosDB.Transaction.add_query(trans3, "FIND ENTITY WITH id=123") == nothing 91 92 end 93 94 @testset "TestEntity" begin 95 ent_with_name = CaosDB.Entity.create_entity("TestEnt") - src/Entity.jl 0 → 100644
8 # it under the terms of the GNU Affero General Public License as 9 # published by the Free Software Foundation, either version 3 of the 10 # License, or (at your option) any later version. 11 # 12 # This program is distributed in the hope that it will be useful, but 13 # WITHOUT ANY WARRANTY; without even the implied warranty of 14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 # Affero General Public License for more details. 16 # 17 # You should have received a copy of the GNU Affero General Public 18 # License along with this program. If not, see 19 # <https://www.gnu.org/licenses/>. 20 # 21 # ** end header 22 # 23 module Entity This one is quite long but also very repetitive (similar to the entity functions in ccaosdb). Even more so, since they are overloaded at many points to increase convenience (allow accessing members with Integers instead of Cints, getting all parents, properties, ... instead of getting them one-by-one, etc.).
- src/Entity.jl 0 → 100644
11 # 12 # This program is distributed in the hope that it will be useful, but 13 # WITHOUT ANY WARRANTY; without even the implied warranty of 14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 # Affero General Public License for more details. 16 # 17 # You should have received a copy of the GNU Affero General Public 18 # License along with this program. If not, see 19 # <https://www.gnu.org/licenses/>. 20 # 21 # ** end header 22 # 23 module Entity 24 25 # Creators 26 export create_entity, It's kind of ugly but for users to be able to type
using Caosdb entity = create_entity()
Instead of having to use
CaosDB.Entity.create_entity()
everywhere, this has to be exported by the Entity submodule first and then again by the CaosDB module itself.Note that this is the Julia way of extending the global namespace with the exported members of used modules in contrast to imported modules (
import CaosDB
instead ofusing CaosDB
). In the latter case, nothing is forwarded to the global namespace even when exported.
- src/Entity.jl 0 → 100644
197 198 if name != "" 199 set_name(entity, name) 200 end 201 202 return entity 203 204 end 205 206 """ 207 function create_recordtype(name::AbstractString = "") 208 209 Return a new entity object with role RecordType. If a `name` was 210 provided, its name is set accordingly. 211 """ 212 function create_recordtype(name::AbstractString = "") - src/Entity.jl 0 → 100644
233 234 return record 235 236 end 237 238 """ 239 function create_property_entity(; 240 name::AbstractString = "", 241 datatype::AbstractString = "", 242 unit::AbstractString = "", 243 ) 244 245 Return a new entity object with role Record. If `name`, `datatype`, or 246 `unit` were provided, its name, datatype, or unit are set accordingly. 247 """ 248 function create_property_entity(; - src/Entity.jl 0 → 100644
268 269 """ 270 function create_property(; 271 name::AbstractString = "", 272 id::AbstractString = "", 273 value::AbstractString = "", 274 ) 275 276 Create a property object that can be appended to another `_Entity`. If 277 `name`, `id`, or `value` are given, the property's name, id, or value 278 are set accordingly. 279 280 !!! info 281 282 This is not an `_Entity` that could be inserted or updated by its 283 own but a `_Property` that is to be appended to another `_Entity`. - src/Entity.jl 0 → 100644
343 set_name(parent, name) 344 end 345 346 if id != "" 347 set_id(parent, id) 348 end 349 350 return parent 351 end 352 353 """ 354 function get_id(entity::Ref{_Entity}) 355 356 Return the id of the given `entity` 357 """ 358 function get_id(entity::Ref{_Entity}) - src/Entity.jl 0 → 100644
345 346 if id != "" 347 set_id(parent, id) 348 end 349 350 return parent 351 end 352 353 """ 354 function get_id(entity::Ref{_Entity}) 355 356 Return the id of the given `entity` 357 """ 358 function get_id(entity::Ref{_Entity}) 359 360 out = Vector{UInt8}(undef, 256) Yet another way of providing a char pointer to a ccall. If the string is written into by the ccall, this is the way to initialize it. The actual value (
undef
) and the actual length (256 was the one all the examples used, so I stuck with it) don't really matter, since memory and the new values are managed by the called c function anyway.changed this line in version 20 of the diff
- src/Entity.jl 0 → 100644
357 """ 358 function get_id(entity::Ref{_Entity}) 359 360 out = Vector{UInt8}(undef, 256) 361 362 err_code = ccall( 363 (:caosdb_entity_entity_get_id, CaosDB.library_name), 364 Cint, 365 (Ref{_Entity}, Ptr{UInt8}), 366 entity, 367 out, 368 ) 369 370 CaosDB.Exceptions.evaluate_return_code(err_code) 371 372 return GC.@preserve out unsafe_string(pointer(out)) changed this line in version 20 of the diff
- src/Entity.jl 0 → 100644
798 """ 799 function get_errors_size(entity::Ref{_Entity}) 800 801 size = Ref{Cint}(0) 802 803 err_code = ccall( 804 (:caosdb_entity_entity_get_errors_size, CaosDB.library_name), 805 Cint, 806 (Ref{_Entity}, Ref{Cint}), 807 entity, 808 size, 809 ) 810 811 CaosDB.Exceptions.evaluate_return_code(err_code) 812 813 return size[] - src/Entity.jl 0 → 100644
847 848 message_text = get_description(error) 849 850 code = get_code(error) 851 852 return CaosDB.Exceptions.CaosDBMessage(message_text, code) 853 end 854 855 """ 856 function get_error(entity::Ref{_Entity}, index::Integer) 857 858 Convenience wrapper for `get_error(::Ref{_Entity}, ::Cint)`. Convert 859 `index` to Int32 and return the error at position `index` of the given 860 `entity`. 861 """ 862 function get_error(entity::Ref{_Entity}, index::Integer) - src/Entity.jl 0 → 100644
828 throw( 829 DomainError( 830 index, 831 "You tried to access the error at position $index but the entity only has $size errors.", 832 ), 833 ) 834 end 835 836 error = Ref{_Message}(_Message()) 837 838 err_code = ccall( 839 (:caosdb_entity_entity_get_error, CaosDB.library_name), 840 Cint, 841 (Ref{_Entity}, Cint), 842 entity, 843 index - Cint(1), - src/Entity.jl 0 → 100644
1222 function get_properties(entity::Ref{_Entity}) 1223 1224 size = get_properties_size(entity) 1225 1226 properties = [get_property(entity, Cint(ii)) for ii = 1:size] 1227 1228 return properties 1229 end 1230 1231 """ 1232 function set_id(entity::Ref{_Entity}, id::AbstractString) 1233 1234 Throws a `CaosDB.Exceptions.ClientException` since you are not allowed 1235 to set the id of entities. 1236 """ 1237 function set_id(entity::Ref{_Entity}, id::AbstractString) - src/Entity.jl 0 → 100644
1498 (:caosdb_entity_entity_append_parent, CaosDB.library_name), 1499 Cint, 1500 (Ref{_Entity}, Ref{_Parent}), 1501 entity, 1502 parent, 1503 ) 1504 1505 CaosDB.Exceptions.evaluate_return_code(err_code) 1506 end 1507 1508 """ 1509 function append_parents(entity::Ref{_Entity}, parents::Vector{Base.RefValue{_Parent}}) 1510 1511 Append all given `parents` to the parents of the given `entity`. 1512 """ 1513 function append_parents(entity::Ref{_Entity}, parents::Vector{Base.RefValue{_Parent}}) - src/Entity.jl 0 → 100644
1581 (:caosdb_entity_entity_remove_property, CaosDB.library_name), 1582 Cint, 1583 (Ref{_Entity}, Cint), 1584 entity, 1585 index - Cint(1), 1586 ) 1587 1588 CaosDB.Exceptions.evaluate_return_code(err_code) 1589 end 1590 1591 """ 1592 function has_errors(entity::Ref{_Entity}) 1593 1594 Return true if the given `entity` has errors, false otherwise. 1595 """ 1596 function has_errors(entity::Ref{_Entity})