From 1544e4791382cc72dd350c155d59cc0d18d1a6d1 Mon Sep 17 00:00:00 2001 From: Kory Draughn Date: Thu, 4 Apr 2024 11:27:16 -0400 Subject: [PATCH] squash. Genquery2Input serialization w/ test. clang-format --- scripts/irods/test/test_dynamic_peps.py | 69 ++++++++++++++++++- .../genquery2_table_column_mappings.hpp | 2 +- server/genquery2/src/genquery2_sql.cpp | 8 +-- server/re/src/irods_re_serialization.cpp | 34 +++++---- 4 files changed, 94 insertions(+), 19 deletions(-) diff --git a/scripts/irods/test/test_dynamic_peps.py b/scripts/irods/test/test_dynamic_peps.py index 48bd5598e6..4871e7f69f 100644 --- a/scripts/irods/test/test_dynamic_peps.py +++ b/scripts/irods/test/test_dynamic_peps.py @@ -420,8 +420,75 @@ def has_avu(): # This isn't required, but we do it here to keep the database metadata # tables small, for performance reasons. self.admin.run_icommand(['imeta', 'rm', '-C', self.admin.session_collection, attr_name_1, delay_rule_body]) - self.admin.run_icommand(['imeta', 'rm', '-C', self.admin.session_collection, attr_name_2, '1']) + self.admin.run_icommand(['imeta', 'rm', '-C', self.admin.session_collection, attr_name_2, '12']) finally: # Restart so the server's original configuration takes effect. IrodsController().restart(test_mode=True) + + @unittest.skipIf(plugin_name != 'irods_rule_engine_plugin-irods_rule_language' or test.settings.RUN_IN_TOPOLOGY, "Requires NREP and single node zone.") + def test_if_Genquery2Input_is_exposed__issue_7570(self): + with temporary_core_file() as core: + avu_prefix = 'test_if_Genquery2Input_is_exposed__issue_7570' + + # Add a rule to core.re which when triggered will add AVUs to the user's + # session collection. + core.add_rule(dedent(''' + pep_api_genquery2_pre(*inst, *comm, *input, *output) {{ + *v = *input.query_string; + msiModAVUMetadata('-C', '{self.user.session_collection}', 'add', '{avu_prefix}_0', *v, ''); + + *v = *input.zone; + if (strlen(*v) > 0) {{ + msiModAVUMetadata('-C', '{self.user.session_collection}', 'add', '{avu_prefix}_1', *v, ''); + }} + else {{ + msiModAVUMetadata('-C', '{self.user.session_collection}', 'add', '{avu_prefix}_1', 'unspecified', ''); + }} + + *v = *input.sql_only; + msiModAVUMetadata('-C', '{self.user.session_collection}', 'add', '{avu_prefix}_2', *v, ''); + + *v = *input.column_mappings; + msiModAVUMetadata('-C', '{self.user.session_collection}', 'add', '{avu_prefix}_3', *v, ''); + }} + '''.format(**locals()))) + + try: + # Trigger PEP, ignore output. + query_string = f"select COLL_NAME where COLL_NAME = '{self.user.session_collection}'" + self.user.assert_icommand(['iquery', query_string], 'STDOUT') + + # Show the session collection has the expected AVUs attached to it. + self.user.assert_icommand(['imeta', 'ls', '-C', self.user.session_collection], 'STDOUT', [ + f'attribute: {avu_prefix}_0\nvalue: {query_string}\n', + f'attribute: {avu_prefix}_1\nvalue: unspecified\n', + f'attribute: {avu_prefix}_2\nvalue: 0\n', + f'attribute: {avu_prefix}_3\nvalue: 0\n', + ]) + + # Remove the AVUs to avoid duplicate AVU errors. These will cause the test to fail. + self.user.assert_icommand(['imeta', 'rm', '-C', self.user.session_collection, f'{avu_prefix}_0', query_string]) + self.user.assert_icommand(['imeta', 'rm', '-C', self.user.session_collection, f'{avu_prefix}_1', 'unspecified']) + self.user.assert_icommand(['imeta', 'rm', '-C', self.user.session_collection, f'{avu_prefix}_2', '0']) + self.user.assert_icommand(['imeta', 'rm', '-C', self.user.session_collection, f'{avu_prefix}_3', '0']) + + # Now, with the zone explicitly passed. + self.user.assert_icommand(['iquery', '-z', self.user.zone_name, query_string], 'STDOUT') + + # Show the session collection has the expected AVUs attached to it. + self.user.assert_icommand(['imeta', 'ls', '-C', self.user.session_collection], 'STDOUT', [ + f'attribute: {avu_prefix}_0\nvalue: {query_string}\n', + f'attribute: {avu_prefix}_1\nvalue: {self.user.zone_name}\n', + f'attribute: {avu_prefix}_2\nvalue: 0\n', + f'attribute: {avu_prefix}_3\nvalue: 0\n', + ]) + + finally: + # This isn't required, but we do it here to keep the database metadata + # tables small, for performance reasons. + self.user.run_icommand(['imeta', 'rm', '-C', self.user.session_collection, f'{avu_prefix}_0', query_string]) + self.user.run_icommand(['imeta', 'rm', '-C', self.user.session_collection, f'{avu_prefix}_1', 'unspecified']) + self.user.run_icommand(['imeta', 'rm', '-C', self.user.session_collection, f'{avu_prefix}_1', self.user.zone_name]) + self.user.run_icommand(['imeta', 'rm', '-C', self.user.session_collection, f'{avu_prefix}_2', '0']) + self.user.run_icommand(['imeta', 'rm', '-C', self.user.session_collection, f'{avu_prefix}_3', '0']) diff --git a/server/genquery2/include/irods/private/genquery2_table_column_mappings.hpp b/server/genquery2/include/irods/private/genquery2_table_column_mappings.hpp index 08007439fa..01555f6b78 100644 --- a/server/genquery2/include/irods/private/genquery2_table_column_mappings.hpp +++ b/server/genquery2/include/irods/private/genquery2_table_column_mappings.hpp @@ -212,7 +212,7 @@ namespace irods::experimental::genquery2 //{"TICKET_EXPIRY_TIME", {"R_TICKET_MAIN", "ticket_expiry_ts"}}, //{"TICKET_CREATE_TIME", {"R_TICKET_MAIN", "create_time"}}, //{"TICKET_MODIFY_TIME", {"R_TICKET_MAIN", "modify_time"}}, - + // Is this possible? Does it make sense? // This isn't a thing in GenQuery1. //{"TICKET_LOGICAL_PATH", {"R_TICKET_MAIN", "modify_time"}}, diff --git a/server/genquery2/src/genquery2_sql.cpp b/server/genquery2/src/genquery2_sql.cpp index bf3979e4ad..9dad38d428 100644 --- a/server/genquery2/src/genquery2_sql.cpp +++ b/server/genquery2/src/genquery2_sql.cpp @@ -586,10 +586,10 @@ namespace alias = _state.table_aliases.at(std::string{iter->second.table}); } - const auto ast_iter = std::find_if( - std::begin(_state.ast_column_ptrs), - std::end(_state.ast_column_ptrs), - [&se](const irods::experimental::genquery2::column* _p) { return _p->name == se.column; }); + const auto ast_iter = + std::find_if(std::begin(_state.ast_column_ptrs), + std::end(_state.ast_column_ptrs), + [&se](const irods::experimental::genquery2::column* _p) { return _p->name == se.column; }); if (std::end(_state.ast_column_ptrs) == ast_iter) { throw std::invalid_argument{"cannot generate SQL from General Query."}; diff --git a/server/re/src/irods_re_serialization.cpp b/server/re/src/irods_re_serialization.cpp index 2d252d30c5..17ae187c54 100644 --- a/server/re/src/irods_re_serialization.cpp +++ b/server/re/src/irods_re_serialization.cpp @@ -1120,26 +1120,28 @@ namespace irods::re_serialization return SUCCESS(); } // serialize_bytesBuf_ptr - - static irods::error serialize_const_Genquery2Input_ptr(boost::any _p, serialized_parameter_t& _out) + static irods::error serialize_Genquery2Input_ptr(boost::any _p, serialized_parameter_t& _out) { try { - auto* v = boost::any_cast(_p); + const auto* v = boost::any_cast(_p); - _out["query_string"] = v->query_string; - _out["zone"] = v->zone; + _out["query_string"] = v->query_string ? v->query_string : ""; + _out["zone"] = v->zone ? v->zone : ""; _out["sql_only"] = std::to_string(v->sql_only); _out["column_mappings"] = std::to_string(v->column_mappings); return SUCCESS(); } catch (const boost::bad_any_cast& e) { - return ERROR(INVALID_ANY_CAST, fmt::format("{}: failed to cast pointer to [std::vector*]: {}", __func__, e.what())); + return ERROR( + INVALID_ANY_CAST, + fmt::format("{}: failed to cast pointer to [std::vector*]: {}", __func__, e.what())); } catch (const std::exception& e) { - return ERROR(SYS_LIBRARY_ERROR, fmt::format("{}: failed to serialize [std::vector*]: {}", __func__, e.what())); + return ERROR(SYS_LIBRARY_ERROR, + fmt::format("{}: failed to serialize [std::vector*]: {}", __func__, e.what())); } - } // serialize_const_Genquery2Input_ptr + } // serialize_Genquery2Input_ptr static void serialize_vector_of_strings_ptr_impl(const std::vector& _v, serialized_parameter_t& _out) { @@ -1164,10 +1166,13 @@ namespace irods::re_serialization return SUCCESS(); } catch (const boost::bad_any_cast& e) { - return ERROR(INVALID_ANY_CAST, fmt::format("{}: failed to cast pointer to [std::vector*]: {}", __func__, e.what())); + return ERROR( + INVALID_ANY_CAST, + fmt::format("{}: failed to cast pointer to [std::vector*]: {}", __func__, e.what())); } catch (const std::exception& e) { - return ERROR(SYS_LIBRARY_ERROR, fmt::format("{}: failed to serialize [std::vector*]: {}", __func__, e.what())); + return ERROR(SYS_LIBRARY_ERROR, + fmt::format("{}: failed to serialize [std::vector*]: {}", __func__, e.what())); } } // serialize_const_vector_of_strings_ptr @@ -1184,10 +1189,13 @@ namespace irods::re_serialization return SUCCESS(); } catch (const boost::bad_any_cast& e) { - return ERROR(INVALID_ANY_CAST, fmt::format("{}: failed to cast pointer to [std::vector*]: {}", __func__, e.what())); + return ERROR( + INVALID_ANY_CAST, + fmt::format("{}: failed to cast pointer to [std::vector*]: {}", __func__, e.what())); } catch (const std::exception& e) { - return ERROR(SYS_LIBRARY_ERROR, fmt::format("{}: failed to serialize [std::vector*]: {}", __func__, e.what())); + return ERROR(SYS_LIBRARY_ERROR, + fmt::format("{}: failed to serialize [std::vector*]: {}", __func__, e.what())); } } // serialize_vector_of_strings_ptr @@ -1245,7 +1253,7 @@ namespace irods::re_serialization { std::type_index(typeid(openedDataObjInp_t*)), serialize_openedDataObjInp_ptr }, { std::type_index(typeid(openedDataObjInp_t**)), serialize_openedDataObjInp_ptr_ptr }, { std::type_index(typeid(bytesBuf_t*)), serialize_bytesBuf_ptr }, - { std::type_index(typeid(const Genquery2Input*)), serialize_const_Genquery2Input_ptr }, + { std::type_index(typeid(Genquery2Input*)), serialize_Genquery2Input_ptr }, { std::type_index(typeid(const std::vector*)), serialize_const_vector_of_strings_ptr }, { std::type_index(typeid(std::vector*)), serialize_vector_of_strings_ptr } };