From 6629a28f83e85faebf00ce6aba494b4773c1d160 Mon Sep 17 00:00:00 2001 From: nicola cabiddu Date: Wed, 14 Sep 2022 10:42:26 +0100 Subject: [PATCH 01/14] Fix appending to list ignores existing query (#5850) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * do not discard existing query for lists * changelog entry * Fix bug. Returned query does not use combined * added unit test Co-authored-by: Kasper Overgård Nielsen --- CHANGELOG.md | 1 + src/realm/object-store/c_api/query.cpp | 8 ++++++-- test/object-store/c_api/c_api.cpp | 27 ++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8544127573..4cb6386bc27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * Fix one cause of QoS inversion warnings when performing writes on the main thread on Apple platforms. Waiting for async notifications to be ready is now done in a QoS-aware ways. * `Realm::refresh()` did not actually advance to the latest version in some cases. If there was a version newer than the current version which did not require blocking it would advance to that instead, contrary to the documented behavior. * Fixed `realm_query_parse_for_results` ignoring query for `query_result_t` passed as parameter ([#5841](https://github.com/realm/realm-core/pull/5841)). +* Fixed `realm_query_parse_for_list` ignoring existing query ([#5850](https://github.com/realm/realm-core/pull/5850)). ### Breaking changes * None. diff --git a/src/realm/object-store/c_api/query.cpp b/src/realm/object-store/c_api/query.cpp index f6545d86573..f570489d6a2 100644 --- a/src/realm/object-store/c_api/query.cpp +++ b/src/realm/object-store/c_api/query.cpp @@ -244,11 +244,15 @@ RLM_API realm_query_t* realm_query_parse_for_list(const realm_list_t* list, cons const realm_query_arg_t* args) { return wrap_err([&]() { + auto existing_query = list->get_query(); auto realm = list->get_realm(); auto table = list->get_table(); auto query = parse_and_apply_query(realm, table, query_string, num_args, args); - auto ordering = query.get_ordering(); - return new realm_query_t{std::move(query), std::move(ordering), realm}; + auto combined = existing_query.and_query(query); + auto ordering_copy = util::make_bind(); + if (auto ordering = query.get_ordering()) + ordering_copy->append(*ordering); + return new realm_query_t{std::move(combined), std::move(ordering_copy), realm}; }); } diff --git a/test/object-store/c_api/c_api.cpp b/test/object-store/c_api/c_api.cpp index 46cbcf33a9f..36a472aa7fb 100644 --- a/test/object-store/c_api/c_api.cpp +++ b/test/object-store/c_api/c_api.cpp @@ -2304,6 +2304,33 @@ TEST_CASE("C API", "[c_api]") { cptr_checked(realm_query_parse_for_list(list.get(), "TRUEPREDICATE", 0, nullptr)); } + SECTION("lists append query") { + auto list = cptr_checked(realm_get_list(obj1.get(), foo_properties["link_list"])); + + auto bar_link = realm_object_as_link(obj2.get()); + realm_value_t bar_link_val; + bar_link_val.type = RLM_TYPE_LINK; + bar_link_val.link = bar_link; + + write([&]() { + CHECK(checked(realm_list_insert(list.get(), 0, bar_link_val))); + CHECK(checked(realm_list_insert(list.get(), 1, bar_link_val))); + CHECK(checked(realm_list_insert(list.get(), 2, bar_link_val))); + }); + + size_t n = 0; + realm_list_size(list.get(), &n); + CHECK(n == 3); + auto query = cptr_checked(realm_query_parse_for_list(list.get(), "TRUEPREDICATE ", 0, nullptr)); + n = 0; + realm_query_count(query.get(), &n); + CHECK(n == 3); + + write([&]() { + realm_list_clear(list.get()); + }); + } + SECTION("combine results query") { realm_value_t int_arg = rlm_int_val(123); const size_t num_args = 1; From a75f8afe95a341a982f92472709194cbc8e2e8c9 Mon Sep 17 00:00:00 2001 From: Sergey Gerasimenko Date: Wed, 14 Sep 2022 16:50:22 +0300 Subject: [PATCH 02/14] Update app.hpp (#5854) --- src/realm/object-store/sync/app.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/realm/object-store/sync/app.hpp b/src/realm/object-store/sync/app.hpp index a0110a39160..fa1b16d1484 100644 --- a/src/realm/object-store/sync/app.hpp +++ b/src/realm/object-store/sync/app.hpp @@ -44,7 +44,7 @@ class App; typedef std::shared_ptr SharedApp; -/// The `App` has the fundamental set of methods for communicating with a MongoDB Realm application backend. +/// The `App` has the fundamental set of methods for communicating with a Atlas App Services backend. /// /// This class provides access to login and authentication. /// From 78bd122d91ab2e1c319dedc0105bfc609a090253 Mon Sep 17 00:00:00 2001 From: nicola cabiddu Date: Wed, 14 Sep 2022 14:56:23 +0100 Subject: [PATCH 03/14] Expose `Obj::get_parent_object` in the C API (#5851) * expose get_object_parent towards c api * entry changelog * Update src/realm.h Co-authored-by: Yavor Georgiev --- CHANGELOG.md | 1 + src/realm.h | 6 ++++++ src/realm/object-store/c_api/object.cpp | 10 ++++++++++ 3 files changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cb6386bc27..7b76b1d4416 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Enhancements * (PR [#????](https://github.com/realm/realm-core/pull/????)) * Convert object_store::Collection types into Results (PR [#5845](https://github.com/realm/realm-core/pull/5845)) +* Expose `realm_object_get_parent` in the C API (PR [#5851](https://github.com/realm/realm-core/pull/5851)) ### Fixed * ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?) diff --git a/src/realm.h b/src/realm.h index b3eb6876c07..2bff945cdfd 100644 --- a/src/realm.h +++ b/src/realm.h @@ -1408,6 +1408,12 @@ RLM_API bool realm_get_num_versions(const realm_t*, uint64_t* out_versions_count */ RLM_API realm_object_t* realm_get_object(const realm_t*, realm_class_key_t class_key, realm_object_key_t obj_key); +/** + * Get the parent object for the object passed as argument. Only works for embedded objects. + * @return true, if no errors occurred. + */ +RLM_API bool realm_object_get_parent(const realm_object_t* object, realm_object_t* parent); + /** * Find an object with a particular primary key value. * diff --git a/src/realm/object-store/c_api/object.cpp b/src/realm/object-store/c_api/object.cpp index 560a4077d78..3fc2387b75b 100644 --- a/src/realm/object-store/c_api/object.cpp +++ b/src/realm/object-store/c_api/object.cpp @@ -28,6 +28,16 @@ RLM_API realm_object_t* realm_get_object(const realm_t* realm, realm_class_key_t }); } +RLM_API bool realm_object_get_parent(const realm_object_t* object, realm_object_t* parent) +{ + return wrap_err([&]() { + if (parent) + *parent = realm_object_t{realm::Object{object->get_realm(), object->obj().get_parent_object()}}; + return true; + }); +} + + RLM_API realm_object_t* realm_object_find_with_primary_key(const realm_t* realm, realm_class_key_t class_key, realm_value_t pk, bool* out_found) { From 267fa66bbc9cfda9a6c2e5930febef9dc0d4fce4 Mon Sep 17 00:00:00 2001 From: James Stone Date: Wed, 14 Sep 2022 10:11:42 -0700 Subject: [PATCH 04/14] Refactor link tracing code (#5796) * refactor link tracing code * LinkTranslator gets a separate file * update changelog * review feedback --- CHANGELOG.md | 1 + Package.swift | 1 + src/realm/CMakeLists.txt | 1 + src/realm/link_translator.cpp | 94 +++++++ src/realm/link_translator.hpp | 54 ++++ src/realm/list.cpp | 7 +- src/realm/obj.cpp | 469 ++++++++++++++++++------------- src/realm/obj.hpp | 2 + src/realm/table.cpp | 30 +- test/object-store/migrations.cpp | 100 +++++++ 10 files changed, 560 insertions(+), 199 deletions(-) create mode 100644 src/realm/link_translator.cpp create mode 100644 src/realm/link_translator.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b76b1d4416..f074afed6dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### Fixed * ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?) +* Prevent migrations to an embedded object type when there are incoming links from Mixed/TypedLink properties until we can support them. ([#5796](https://github.com/realm/realm-core/pull/5796)) * Fix a data race on RealmCoordinator::m_sync_session which could occur if multiple threads performed the initial open of a Realm at once. (since v11.8.0). * If a SyncSession outlived the parent Realm and then was adopted by a new Realm for the same file, other processes would not get notified for sync writes on that file. * Fix one cause of QoS inversion warnings when performing writes on the main thread on Apple platforms. Waiting for async notifications to be ready is now done in a QoS-aware ways. diff --git a/Package.swift b/Package.swift index 73bb28f833e..1f11542b1ae 100644 --- a/Package.swift +++ b/Package.swift @@ -75,6 +75,7 @@ let notSyncServerSources: [String] = [ "realm/history.cpp", "realm/impl", "realm/index_string.cpp", + "realm/link_translator.cpp", "realm/list.cpp", "realm/mixed.cpp", "realm/node.cpp", diff --git a/src/realm/CMakeLists.txt b/src/realm/CMakeLists.txt index 993ca38fc08..6f211fb0a51 100644 --- a/src/realm/CMakeLists.txt +++ b/src/realm/CMakeLists.txt @@ -41,6 +41,7 @@ set(REALM_SOURCES impl/simulated_failure.cpp impl/transact_log.cpp index_string.cpp + link_translator.cpp list.cpp node.cpp mixed.cpp diff --git a/src/realm/link_translator.cpp b/src/realm/link_translator.cpp new file mode 100644 index 00000000000..253ad68b89c --- /dev/null +++ b/src/realm/link_translator.cpp @@ -0,0 +1,94 @@ +/************************************************************************* + * + * Copyright 2022 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#include "realm/link_translator.hpp" + +#include +#include +#include + +namespace realm { + +LinkTranslator::LinkTranslator(Obj origin, ColKey origin_col_key) + : m_origin_obj(origin) + , m_origin_col_key(origin_col_key) +{ +} + +void LinkTranslator::run() +{ + ColumnAttrMask attr = m_origin_col_key.get_attrs(); + if (attr.test(col_attr_List)) { + if (m_origin_col_key.get_type() == col_type_LinkList) { + LnkLst link_list = m_origin_obj.get_linklist(m_origin_col_key); + on_list_of_links(link_list); + } + else if (m_origin_col_key.get_type() == col_type_Mixed) { + Lst list = m_origin_obj.get_list(m_origin_col_key); + on_list_of_mixed(list); + } + else if (m_origin_col_key.get_type() == col_type_TypedLink) { + Lst list = m_origin_obj.get_list(m_origin_col_key); + on_list_of_typedlink(list); + } + else { + throw std::runtime_error( + util::format("LinkTranslator unhandled list type: %1", m_origin_col_key.get_type())); + } + } + else if (attr.test(col_attr_Set)) { + if (m_origin_col_key.get_type() == col_type_Link) { + LnkSet set = m_origin_obj.get_linkset(m_origin_col_key); + on_set_of_links(set); + } + else if (m_origin_col_key.get_type() == col_type_Mixed) { + Set set = m_origin_obj.get_set(m_origin_col_key); + on_set_of_mixed(set); + } + else if (m_origin_col_key.get_type() == col_type_TypedLink) { + Set set = m_origin_obj.get_set(m_origin_col_key); + on_set_of_typedlink(set); + } + else { + throw std::runtime_error( + util::format("LinkTranslator unhandled set type: %1", m_origin_col_key.get_type())); + } + } + else if (attr.test(col_attr_Dictionary)) { + auto dict = m_origin_obj.get_dictionary(m_origin_col_key); + on_dictionary(dict); + } + else { + REALM_ASSERT_EX(!m_origin_col_key.is_collection(), m_origin_col_key); + if (m_origin_col_key.get_type() == col_type_Link) { + on_link_property(m_origin_col_key); + } + else if (m_origin_col_key.get_type() == col_type_Mixed) { + on_mixed_property(m_origin_col_key); + } + else if (m_origin_col_key.get_type() == col_type_TypedLink) { + on_typedlink_property(m_origin_col_key); + } + else { + throw std::runtime_error( + util::format("LinkTranslator unhandled property type: %1", m_origin_col_key.get_type())); + } + } +} + +} // namespace realm diff --git a/src/realm/link_translator.hpp b/src/realm/link_translator.hpp new file mode 100644 index 00000000000..27039eb8abf --- /dev/null +++ b/src/realm/link_translator.hpp @@ -0,0 +1,54 @@ +/************************************************************************* + * + * Copyright 2022 Realm Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **************************************************************************/ + +#ifndef REALM_LINK_TRANSLATOR_HPP +#define REALM_LINK_TRANSLATOR_HPP + +#include + +namespace realm { + +// This construct is used when code needs to handle all +// possible link column types. Subclass and override all +// methods to handle each type. A good example of where +// this is useful is when following a backlink to its +// origin column and modifying the outgoing link from +// whatever container it came from. +class LinkTranslator { +public: + LinkTranslator(Obj origin, ColKey origin_col_key); + void run(); + virtual void on_list_of_links(LnkLst& list) = 0; + virtual void on_list_of_mixed(Lst& list) = 0; + virtual void on_list_of_typedlink(Lst& list) = 0; + virtual void on_set_of_links(LnkSet& set) = 0; + virtual void on_set_of_mixed(Set& set) = 0; + virtual void on_set_of_typedlink(Set& set) = 0; + virtual void on_dictionary(Dictionary& dict) = 0; + virtual void on_link_property(ColKey col) = 0; + virtual void on_mixed_property(ColKey col) = 0; + virtual void on_typedlink_property(ColKey col) = 0; + +protected: + Obj m_origin_obj; + ColKey m_origin_col_key; +}; + +} // namespace realm + +#endif // REALM_LINK_TRANSLATOR_HPP diff --git a/src/realm/list.cpp b/src/realm/list.cpp index b2b40d909ef..359d59fe6c1 100644 --- a/src/realm/list.cpp +++ b/src/realm/list.cpp @@ -278,10 +278,9 @@ void Lst::do_clear() ObjKey target_key = m_tree->get(ndx); Obj target_obj = target_table->get_object(target_key); target_obj.remove_one_backlink(backlink_col, m_obj.get_key()); // Throws - size_t num_remaining = target_obj.get_backlink_count(*origin_table, m_col_key); - if (num_remaining == 0) { - state.m_to_be_deleted.emplace_back(target_table_key, target_key); - } + // embedded objects should only have one incoming link + REALM_ASSERT_EX(target_obj.get_backlink_count() == 0, target_obj.get_backlink_count()); + state.m_to_be_deleted.emplace_back(target_table_key, target_key); } m_tree->clear(); diff --git a/src/realm/obj.cpp b/src/realm/obj.cpp index afed9725eee..fac418b970f 100644 --- a/src/realm/obj.cpp +++ b/src/realm/obj.cpp @@ -32,6 +32,7 @@ #include "realm/cluster_tree.hpp" #include "realm/column_type_traits.hpp" #include "realm/dictionary.hpp" +#include "realm/link_translator.hpp" #include "realm/index_string.hpp" #include "realm/object_converter.hpp" #include "realm/replication.hpp" @@ -716,6 +717,61 @@ size_t Obj::get_backlink_cnt(ColKey backlink_col) const void Obj::traverse_path(Visitor v, PathSizer ps, size_t path_length) const { + struct BacklinkTraverser : public LinkTranslator { + BacklinkTraverser(Obj origin, ColKey origin_col_key, Obj dest) + : LinkTranslator(origin, origin_col_key) + , m_dest_obj(dest) + { + } + void on_list_of_links(LnkLst& ll) final + { + auto i = ll.find_first(m_dest_obj.get_key()); + REALM_ASSERT(i != realm::npos); + m_index = Mixed(int64_t(i)); + } + void on_dictionary(Dictionary& dict) final + { + for (auto it : dict) { + if (it.second.is_type(type_TypedLink) && it.second.get_link() == m_dest_obj.get_link()) { + m_index = it.first; + break; + } + } + REALM_ASSERT(!m_index.is_null()); + } + void on_list_of_mixed(Lst&) final + { + REALM_UNREACHABLE(); // we don't support Mixed link to embedded object yet + } + void on_list_of_typedlink(Lst&) final + { + REALM_UNREACHABLE(); // we don't support TypedLink to embedded object yet + } + void on_set_of_links(LnkSet&) final + { + REALM_UNREACHABLE(); // sets of embedded objects are not allowed at the schema level + } + void on_set_of_mixed(Set&) final + { + REALM_UNREACHABLE(); // we don't support Mixed link to embedded object yet + } + void on_set_of_typedlink(Set&) final + { + REALM_UNREACHABLE(); // we don't support TypedLink to embedded object yet + } + void on_link_property(ColKey) final {} + void on_mixed_property(ColKey) final {} + void on_typedlink_property(ColKey) final {} + Mixed result() + { + return m_index; + } + + private: + Mixed m_index; + Obj m_dest_obj; + }; + if (m_table->is_embedded()) { REALM_ASSERT(get_backlink_count() == 1); m_table->for_each_backlink_column([&](ColKey col_key) { @@ -724,23 +780,9 @@ void Obj::traverse_path(Visitor v, PathSizer ps, size_t path_length) const TableRef tr = m_table->get_opposite_table(col_key); Obj obj = tr->get_object(backlinks[0]); // always the first (and only) auto next_col_key = m_table->get_opposite_column(col_key); - Mixed index; - if (next_col_key.get_attrs().test(col_attr_List)) { - auto ll = obj.get_linklist(next_col_key); - auto i = ll.find_first(get_key()); - REALM_ASSERT(i != realm::npos); - index = Mixed(int64_t(i)); - } - else if (next_col_key.get_attrs().test(col_attr_Dictionary)) { - auto dict = obj.get_dictionary(next_col_key); - for (auto it : dict) { - if (it.second.is_type(type_TypedLink) && it.second.get_link() == get_link()) { - index = it.first; - break; - } - } - REALM_ASSERT(!index.is_null()); - } + BacklinkTraverser traverser{obj, next_col_key, *this}; + traverser.run(); + Mixed index = traverser.result(); obj.traverse_path(v, ps, path_length + 1); v(obj, next_col_key, index); return true; // early out @@ -1798,92 +1840,89 @@ inline void nullify_set(Obj& obj, ColKey origin_col_key, T target) } } // namespace +template +inline void Obj::nullify_single_link(ColKey col, ValueType target) +{ + ColKey::Idx origin_col_ndx = col.get_index(); + Allocator& alloc = get_alloc(); + Array fallback(alloc); + Array& fields = get_tree_top()->get_fields_accessor(fallback, m_mem); + using ArrayType = typename ColumnTypeTraits::cluster_leaf_type; + ArrayType links(alloc); + links.set_parent(&fields, origin_col_ndx.val + 1); + links.init_from_parent(); + // Ensure we are nullifying correct link + REALM_ASSERT(links.get(m_row_ndx) == target); + links.set(m_row_ndx, ValueType{}); + sync(fields); + + if (Replication* repl = get_replication()) + repl->nullify_link(m_table.unchecked_ptr(), col, + m_key); // Throws +} + void Obj::nullify_link(ColKey origin_col_key, ObjLink target_link) && { REALM_ASSERT(get_alloc().get_storage_version() == m_storage_version); - ColKey::Idx origin_col_ndx = origin_col_key.get_index(); - Allocator& alloc = get_alloc(); - - ColumnAttrMask attr = origin_col_key.get_attrs(); - if (attr.test(col_attr_List)) { - if (origin_col_key.get_type() == col_type_LinkList) { - nullify_linklist(*this, origin_col_key, target_link.get_obj_key()); + struct LinkNullifier : public LinkTranslator { + LinkNullifier(Obj origin_obj, ColKey origin_col, ObjLink target) + : LinkTranslator(origin_obj, origin_col) + , m_target_link(target) + { } - else if (origin_col_key.get_type() == col_type_TypedLink) { - nullify_linklist(*this, origin_col_key, target_link); + void on_list_of_links(LnkLst&) final + { + nullify_linklist(m_origin_obj, m_origin_col_key, m_target_link.get_obj_key()); } - else if (origin_col_key.get_type() == col_type_Mixed) { - nullify_linklist(*this, origin_col_key, Mixed(target_link)); + void on_list_of_mixed(Lst&) final + { + nullify_linklist(m_origin_obj, m_origin_col_key, Mixed(m_target_link)); } - else { - REALM_ASSERT(false); + void on_list_of_typedlink(Lst&) final + { + nullify_linklist(m_origin_obj, m_origin_col_key, m_target_link); } - } - else if (attr.test(col_attr_Set)) { - if (origin_col_key.get_type() == col_type_Link) { - nullify_set(*this, origin_col_key, target_link.get_obj_key()); + void on_set_of_links(LnkSet&) final + { + nullify_set(m_origin_obj, m_origin_col_key, m_target_link.get_obj_key()); } - else if (origin_col_key.get_type() == col_type_TypedLink) { - nullify_set(*this, origin_col_key, target_link); + void on_set_of_mixed(Set&) final + { + nullify_set(m_origin_obj, m_origin_col_key, Mixed(m_target_link)); } - else if (origin_col_key.get_type() == col_type_Mixed) { - nullify_set(*this, origin_col_key, Mixed(target_link)); + void on_set_of_typedlink(Set&) final + { + nullify_set(m_origin_obj, m_origin_col_key, m_target_link); } - else { - REALM_ASSERT(false); - } - } - else if (attr.test(col_attr_Dictionary)) { - auto dict = this->get_dictionary(origin_col_key); - Mixed val{target_link}; - for (auto it : dict) { - if (it.second == val) { - dict.nullify(it.first); + void on_dictionary(Dictionary& dict) final + { + Mixed val{m_target_link}; + for (auto it : dict) { + if (it.second == val) { + dict.nullify(it.first); + } } } - } - else { - Array fallback(alloc); - Array& fields = get_tree_top()->get_fields_accessor(fallback, m_mem); - - if (origin_col_key.get_type() == col_type_Link) { - ArrayKey links(alloc); - links.set_parent(&fields, origin_col_ndx.val + 1); - links.init_from_parent(); - - // Ensure we are nullifying correct link - REALM_ASSERT(links.get(m_row_ndx) == target_link.get_obj_key()); - - links.set(m_row_ndx, ObjKey{}); + void on_link_property(ColKey origin_col_key) final + { + m_origin_obj.nullify_single_link(origin_col_key, m_target_link.get_obj_key()); } - else if (origin_col_key.get_type() == col_type_TypedLink) { - ArrayTypedLink links(alloc); - links.set_parent(&fields, origin_col_ndx.val + 1); - links.init_from_parent(); - - // Ensure we are nullifying correct link - REALM_ASSERT(links.get(m_row_ndx) == target_link); - - links.set(m_row_ndx, ObjLink{}); + void on_mixed_property(ColKey origin_col_key) final + { + m_origin_obj.nullify_single_link(origin_col_key, Mixed{m_target_link}); } - else { - ArrayMixed mixed(alloc); - mixed.set_parent(&fields, origin_col_ndx.val + 1); - mixed.init_from_parent(); - - // Ensure we are nullifying correct link - REALM_ASSERT(mixed.get(m_row_ndx).get() == target_link); - - mixed.set(m_row_ndx, Mixed{}); + void on_typedlink_property(ColKey origin_col_key) final + { + m_origin_obj.nullify_single_link(origin_col_key, m_target_link); } - sync(fields); + private: + ObjLink m_target_link; + } nullifier{*this, origin_col_key, target_link}; + nullifier.run(); - if (Replication* repl = get_replication()) - repl->nullify_link(m_table.unchecked_ptr(), origin_col_key, m_key); // Throws - } - alloc.bump_content_version(); + get_alloc().bump_content_version(); } void Obj::set_backlink(ColKey col_key, ObjLink new_link) const @@ -1954,6 +1993,80 @@ bool Obj::remove_backlink(ColKey col_key, ObjLink old_link, CascadeState& state) return false; } +struct EmbeddedObjectLinkMigrator : public LinkTranslator { + EmbeddedObjectLinkMigrator(Obj origin, ColKey origin_col, Obj dest_orig, Obj dest_replace) + : LinkTranslator(origin, origin_col) + , m_dest_orig(dest_orig) + , m_dest_replace(dest_replace) + { + } + void on_list_of_links(LnkLst& list) final + { + auto n = list.find_first(m_dest_orig.get_key()); + REALM_ASSERT(n != realm::npos); + list.set(n, m_dest_replace.get_key()); + } + void on_dictionary(Dictionary& dict) final + { + auto pos = dict.find_any(m_dest_orig.get_link()); + REALM_ASSERT(pos != realm::npos); + Mixed key = dict.get_key(pos); + dict.insert(key, m_dest_replace.get_link()); + } + void on_link_property(ColKey col) final + { + REALM_ASSERT(!m_origin_obj.get(col) || m_origin_obj.get(col) == m_dest_orig.get_key()); + m_origin_obj.set(col, m_dest_replace.get_key()); + } + void on_set_of_links(LnkSet&) final + { + // this should never happen because sets of embedded objects are not allowed at the schema level + REALM_UNREACHABLE(); + } + // The following cases have support here but are expected to fail later on in the + // migration due to core not yet supporting untyped Mixed links to embedded objects. + void on_set_of_mixed(Set& set) final + { + auto did_erase_pair = set.erase(m_dest_orig.get_link()); + REALM_ASSERT(did_erase_pair.second); + set.insert(m_dest_replace.get_link()); + } + void on_set_of_typedlink(Set& set) final + { + auto did_erase_pair = set.erase(m_dest_orig.get_link()); + REALM_ASSERT(did_erase_pair.second); + set.insert(m_dest_replace.get_link()); + } + void on_list_of_mixed(Lst& list) final + { + auto n = list.find_any(m_dest_orig.get_link()); + REALM_ASSERT(n != realm::npos); + list.insert_any(n, m_dest_replace.get_link()); + } + void on_list_of_typedlink(Lst& list) final + { + auto n = list.find_any(m_dest_orig.get_link()); + REALM_ASSERT(n != realm::npos); + list.insert_any(n, m_dest_replace.get_link()); + } + void on_mixed_property(ColKey col) final + { + REALM_ASSERT(m_origin_obj.get(col).is_null() || + m_origin_obj.get(col) == m_dest_orig.get_link()); + m_origin_obj.set_any(col, m_dest_replace.get_link()); + } + void on_typedlink_property(ColKey col) final + { + REALM_ASSERT(m_origin_obj.get(col).is_null() || + m_origin_obj.get(col) == m_dest_orig.get_link()); + m_origin_obj.set(col, m_dest_replace.get_link()); + } + +private: + Obj m_dest_orig; + Obj m_dest_replace; +}; + void Obj::handle_multiple_backlinks_during_schema_migration() { REALM_ASSERT(!m_table->get_primary_key_column()); @@ -1967,7 +2080,9 @@ void Obj::handle_multiple_backlinks_during_schema_migration() auto obj = m_table->create_object(); embedded_obj_tracker->track(*this, obj); auto linking_obj = opposite_table->get_object(backlink); - fix_linking_object_during_schema_migration(linking_obj, obj, opposite_column); + // change incoming links to point to the newly created object + EmbeddedObjectLinkMigrator migrator{linking_obj, opposite_column, *this, obj}; + migrator.run(); } embedded_obj_tracker->process_pending(); return false; @@ -1975,47 +2090,6 @@ void Obj::handle_multiple_backlinks_during_schema_migration() m_table->for_each_backlink_column(copy_links); } -void Obj::fix_linking_object_during_schema_migration(Obj linking_obj, Obj obj, ColKey opposite_col_key) const -{ - // fix linking obj links in order to point to the new object just created - if (opposite_col_key.get_type() == col_type_LinkList) { - auto linking_obj_list = linking_obj.get_linklist(opposite_col_key); - auto n = linking_obj_list.find_first(get_key()); - REALM_ASSERT(n != realm::npos); - linking_obj_list.set(n, obj.get_key()); - } - else if (opposite_col_key.get_attrs().test(col_attr_List)) { - auto linking_obj_list = linking_obj.get_listbase_ptr(opposite_col_key); - auto n = linking_obj_list->find_any(ObjLink{m_table->get_key(), get_key()}); - REALM_ASSERT(n != realm::npos); - linking_obj_list->insert_any(n, ObjLink{m_table->get_key(), obj.get_key()}); - } - else if (opposite_col_key.get_attrs().test(col_attr_Set)) { - // this SHOULD NEVER HAPPEN, since SET sematincs forbids to have a backlink to the same object store - // multiple times. update_schema would have thrown way before to reach this point. - REALM_UNREACHABLE(); - } - else if (opposite_col_key.get_attrs().test(col_attr_Dictionary)) { - auto linking_obj_dictionary = linking_obj.get_dictionary_ptr(opposite_col_key); - auto pos = linking_obj_dictionary->find_any(ObjLink{m_table->get_key(), get_key()}); - REALM_ASSERT(pos != realm::npos); - Mixed key = linking_obj_dictionary->get_key(pos); - linking_obj_dictionary->insert(key, ObjLink{m_table->get_key(), obj.get_key()}); - } - else if (opposite_col_key.get_type() == col_type_Mixed && - linking_obj.get_any(opposite_col_key).get_type() == type_TypedLink) { - REALM_ASSERT(!linking_obj.get(opposite_col_key) || - linking_obj.get(opposite_col_key) == get_key()); - linking_obj.set_any(opposite_col_key, ObjLink{m_table->get_key(), obj.get_key()}); - } - else if (opposite_col_key.get_type() == col_type_Link) { - // Single link - REALM_ASSERT(!linking_obj.get(opposite_col_key) || - linking_obj.get(opposite_col_key) == get_key()); - linking_obj.set(opposite_col_key, obj.get_key()); - } -} - Dictionary Obj::get_dictionary(ColKey col_key) const { REALM_ASSERT(col_key.is_dictionary()); @@ -2069,6 +2143,83 @@ LinkCollectionPtr Obj::get_linkcollection_ptr(ColKey col_key) const void Obj::assign_pk_and_backlinks(const Obj& other) { + struct LinkReplacer : LinkTranslator { + LinkReplacer(Obj origin, ColKey origin_col_key, const Obj& dest_orig, const Obj& dest_replace) + : LinkTranslator(origin, origin_col_key) + , m_dest_orig(dest_orig) + , m_dest_replace(dest_replace) + { + } + void on_list_of_links(LnkLst&) final + { + // using Lst for direct access without hiding unresolved keys + auto list = m_origin_obj.get_list(m_origin_col_key); + auto n = list.find_first(m_dest_orig.get_key()); + REALM_ASSERT(n != realm::npos); + list.set(n, m_dest_replace.get_key()); + } + void on_list_of_mixed(Lst& list) final + { + auto n = list.find_first(m_dest_orig.get_link()); + REALM_ASSERT(n != realm::npos); + list.set(n, m_dest_replace.get_link()); + } + void on_list_of_typedlink(Lst& list) final + { + auto n = list.find_first(m_dest_orig.get_link()); + REALM_ASSERT(n != realm::npos); + list.set(n, m_dest_replace.get_link()); + } + void on_set_of_links(LnkSet&) final + { + // using Set for direct access without hiding unresolved keys + auto set = m_origin_obj.get_set(m_origin_col_key); + set.erase(m_dest_orig.get_key()); + set.insert(m_dest_replace.get_key()); + } + void on_set_of_mixed(Set& set) final + { + set.erase(m_dest_orig.get_link()); + set.insert(m_dest_replace.get_link()); + } + void on_set_of_typedlink(Set& set) final + { + set.erase(m_dest_orig.get_link()); + set.insert(m_dest_replace.get_link()); + } + void on_dictionary(Dictionary& dict) final + { + Mixed val(m_dest_orig.get_link()); + for (auto it : dict) { + if (it.second == val) { + auto link = m_dest_replace.get_link(); + dict.insert(it.first, link); + } + } + } + void on_link_property(ColKey col) final + { + REALM_ASSERT(!m_origin_obj.get(col) || m_origin_obj.get(col) == m_dest_orig.get_key()); + m_origin_obj.set(col, m_dest_replace.get_key()); + } + void on_mixed_property(ColKey col) final + { + REALM_ASSERT(m_origin_obj.get_any(col).is_null() || + m_origin_obj.get_any(col).get_link().get_obj_key() == m_dest_orig.get_key()); + m_origin_obj.set(col, Mixed{m_dest_replace.get_link()}); + } + void on_typedlink_property(ColKey col) final + { + REALM_ASSERT(m_origin_obj.get_any(col).is_null() || + m_origin_obj.get_any(col).get_link().get_obj_key() == m_dest_orig.get_key()); + m_origin_obj.set(col, m_dest_replace.get_link()); + } + + private: + const Obj& m_dest_orig; + const Obj& m_dest_replace; + }; + REALM_ASSERT(get_table() == other.get_table()); if (auto col_pk = m_table->get_primary_key_column()) { Mixed val = other.get_any(col_pk); @@ -2086,68 +2237,8 @@ void Obj::assign_pk_and_backlinks(const Obj& other) auto backlinks = other.get_all_backlinks(col); for (auto bl : backlinks) { auto linking_obj = t->get_object(bl); - if (c.is_dictionary()) { - auto dict = linking_obj.get_dictionary(c); - Mixed val(other.get_link()); - for (auto it : dict) { - if (it.second == val) { - auto link = get_link(); - dict.insert(it.first, link); - } - } - } - else if (c.is_set()) { - if (c.get_type() == col_type_Link) { - auto set = linking_obj.get_set(c); - set.erase(other.get_key()); - set.insert(get_key()); - } - else if (c.get_type() == col_type_TypedLink) { - auto set = linking_obj.get_set(c); - set.erase({m_table->get_key(), other.get_key()}); - set.insert({m_table->get_key(), get_key()}); - } - if (c.get_type() == col_type_Mixed) { - auto set = linking_obj.get_set(c); - set.erase(ObjLink{m_table->get_key(), other.get_key()}); - set.insert(ObjLink{m_table->get_key(), get_key()}); - } - } - else if (c.is_list()) { - if (c.get_type() == col_type_Mixed) { - auto l = linking_obj.get_list(c); - auto n = l.find_first(ObjLink{m_table->get_key(), other.get_key()}); - REALM_ASSERT(n != realm::npos); - l.set(n, ObjLink{m_table->get_key(), get_key()}); - } - else if (c.get_type() == col_type_LinkList) { - // Link list - auto l = linking_obj.get_list(c); - auto n = l.find_first(other.get_key()); - REALM_ASSERT(n != realm::npos); - l.set(n, get_key()); - } - else { - REALM_UNREACHABLE(); // missing type handling - } - } - else { - REALM_ASSERT(!c.is_collection()); - if (c.get_type() == col_type_Link) { - // Single link - REALM_ASSERT(!linking_obj.get(c) || linking_obj.get(c) == other.get_key()); - linking_obj.set(c, get_key()); - } - else if (c.get_type() == col_type_Mixed) { - // Mixed link - REALM_ASSERT(linking_obj.get_any(c).is_null() || - linking_obj.get_any(c).get_link().get_obj_key() == other.get_key()); - linking_obj.set(c, Mixed{ObjLink{m_table->get_key(), get_key()}}); - } - else { - REALM_UNREACHABLE(); // missing type handling - } - } + LinkReplacer replacer{linking_obj, c, other, *this}; + replacer.run(); } return false; }; diff --git a/src/realm/obj.hpp b/src/realm/obj.hpp index 46405c35b86..b36359ac942 100644 --- a/src/realm/obj.hpp +++ b/src/realm/obj.hpp @@ -427,6 +427,8 @@ class Obj { bool remove_backlink(ColKey col_key, ObjLink old_link, CascadeState& state) const; template inline void set_spec(T&, ColKey); + template + inline void nullify_single_link(ColKey col, ValueType target); void fix_linking_object_during_schema_migration(Obj linking_obj, Obj obj, ColKey opposite_col_key) const; }; diff --git a/src/realm/table.cpp b/src/realm/table.cpp index 665dcde4954..288949c97a5 100644 --- a/src/realm/table.cpp +++ b/src/realm/table.cpp @@ -333,7 +333,6 @@ void LinkChain::add(ColKey ck) m_link_cols.push_back(ck); } - // -- Table --------------------------------------------------------------------------------- ColKey Table::add_column(DataType type, StringData name, bool nullable) @@ -1115,15 +1114,34 @@ void Table::set_embedded(bool embedded) } else if (size() > 0) { for (auto object : *this) { - size_t backlink_count = object.get_backlink_count(); + size_t backlink_count = 0; + for_each_backlink_column([&](ColKey backlink_col_key) { + size_t cur_backlinks = object.get_backlink_cnt(backlink_col_key); + if (cur_backlinks > 0) { + // Make sure this link is not an untyped ObjLink which lacks core support in many places + ColKey source_col = get_opposite_column(backlink_col_key); + REALM_ASSERT(source_col); // backlink columns should always have a source + TableRef source_table = get_opposite_table(backlink_col_key); + ColKey forward_col_mapped = source_table->get_opposite_column(source_col); + if (!forward_col_mapped) { + throw std::logic_error(util::format("There is a dynamic/untyped link from a Mixed property " + "'%1.%2' which prevents migrating class '%3' to embedded", + source_table->get_name(), + source_table->get_column_name(source_col), get_name())); + } + } + backlink_count += cur_backlinks; + if (backlink_count > 1) { + throw std::logic_error( + util::format("At least one object in '%1' does have multiple backlinks.", get_name())); + } + return false; // continue + }); + if (backlink_count == 0) { throw std::logic_error(util::format( "At least one object in '%1' does not have a backlink (data would get lost).", get_name())); } - else if (backlink_count > 1) { - throw std::logic_error( - util::format("At least one object in '%1' does have multiple backlinks.", get_name())); - } } } diff --git a/test/object-store/migrations.cpp b/test/object-store/migrations.cpp index 57e60ebe19c..044f69dc4ab 100644 --- a/test/object-store/migrations.cpp +++ b/test/object-store/migrations.cpp @@ -19,6 +19,7 @@ #include #include "util/test_file.hpp" +#include "util/test_utils.hpp" #include #include @@ -696,6 +697,105 @@ TEST_CASE("migration: Automatic") { parent_object.set_property_value(context, "child_property", std::any(child_object)); })); } + + SECTION("Migrations to embedded object with untyped Mixed links") { + auto setup_mixed_link = [&](PropertyType type) -> SharedRealm { + InMemoryTestFile config; + config.automatic_handle_backlicks_in_migrations = true; + Schema schema = { + { + "child_table", + {{"value", PropertyType::Int}}, + }, + {"parent_table", + { + {"child_property", type}, + }}, + }; + auto realm = Realm::get_shared_realm(config); + realm->update_schema(schema, 1); + realm->begin_transaction(); + auto child_table = ObjectStore::table_for_object_type(realm->read_group(), "child_table"); + Obj child_object = child_table->create_object(); + child_object.set("value", 42); + auto parent_table = ObjectStore::table_for_object_type(realm->read_group(), "parent_table"); + auto parent_object = parent_table->create_object(); + auto child_object_key = child_object.get_key(); + ColKey child_col_key = parent_table->get_column_key("child_property"); + + REALM_ASSERT(child_col_key.get_type() == col_type_Mixed); + Mixed child_link = ObjLink{child_table->get_key(), child_object_key}; + if (child_col_key.is_set()) { + auto set = parent_object.get_set(child_col_key); + set.insert(child_link); + } + else if (child_col_key.is_list()) { + auto list = parent_object.get_list(child_col_key); + list.insert(0, child_link); + list.insert(1, child_link); + } + else if (child_col_key.is_dictionary()) { + auto dict = parent_object.get_dictionary(child_col_key); + dict.insert("foo", child_link); + dict.insert("bar", child_link); + } + else { + REALM_ASSERT(!child_col_key.is_collection()); + parent_object.set_any(child_col_key, child_link); + } + realm->commit_transaction(); + REQUIRE(parent_table->size() == 1); + REQUIRE(child_table->size() == 1); + REQUIRE_FALSE(child_table->is_embedded()); + REQUIRE_FALSE(parent_table->is_embedded()); + return realm; + }; + auto post_check_failed_migration = [](SharedRealm realm) { + auto parent_table = ObjectStore::table_for_object_type(realm->read_group(), "parent_table"); + auto child_table = ObjectStore::table_for_object_type(realm->read_group(), "child_table"); + REQUIRE(realm->schema_version() == 1); + REQUIRE(parent_table->size() == 1); + REQUIRE(child_table->size() == 1); + REQUIRE(!child_table->is_embedded()); + }; + const std::string expected_message = + "There is a dynamic/untyped link from a Mixed property 'class_parent_table.child_property' which " + "prevents migrating class 'class_child_table' to embedded"; + + SECTION("List") { + auto realm = setup_mixed_link(PropertyType::Mixed | PropertyType::Nullable | PropertyType::Array); + REQUIRE_THROWS_CONTAINING( + realm->update_schema(set_table_type(realm->schema(), "child_table", ObjectType::Embedded), 2, + [](auto, auto, auto&) {}), + expected_message); + post_check_failed_migration(realm); + } + SECTION("Set") { + auto realm = setup_mixed_link(PropertyType::Mixed | PropertyType::Nullable | PropertyType::Set); + REQUIRE_THROWS_CONTAINING( + realm->update_schema(set_table_type(realm->schema(), "child_table", ObjectType::Embedded), 2, + [](auto, auto, auto&) {}), + expected_message); + post_check_failed_migration(realm); + } + SECTION("Dictionary") { + auto realm = + setup_mixed_link(PropertyType::Mixed | PropertyType::Nullable | PropertyType::Dictionary); + REQUIRE_THROWS_CONTAINING( + realm->update_schema(set_table_type(realm->schema(), "child_table", ObjectType::Embedded), 2, + [](auto, auto, auto&) {}), + expected_message); + post_check_failed_migration(realm); + } + SECTION("Mixed property") { + auto realm = setup_mixed_link(PropertyType::Mixed | PropertyType::Nullable); + REQUIRE_THROWS_CONTAINING( + realm->update_schema(set_table_type(realm->schema(), "child_table", ObjectType::Embedded), 2, + [](auto, auto, auto&) {}), + expected_message); + post_check_failed_migration(realm); + } + } } SECTION("valid migrations") { From e7e2b8a6703408f9b53f68ef43e1d4dbbd578972 Mon Sep 17 00:00:00 2001 From: Daniel Tabacaru <96778637+danieltabacaru@users.noreply.github.com> Date: Wed, 14 Sep 2022 20:40:25 +0300 Subject: [PATCH 05/14] Do not allow asymmetric tables in pbs (#5853) * Differentiate between pbs and flx in schema validation * Disallow asymmetric tables in pbs * Integration and unit tests * Replace uint64_t type with SchemaValidationMode in validate methods * Fix compilation error on build configurations without sync * Update changelog --- CHANGELOG.md | 1 + src/realm.h | 5 +++-- src/realm/object-store/c_api/schema.cpp | 2 +- src/realm/object-store/object_schema.cpp | 10 +++++++++- src/realm/object-store/object_schema.hpp | 3 ++- src/realm/object-store/schema.cpp | 5 ++--- src/realm/object-store/schema.hpp | 4 ++-- src/realm/object-store/shared_realm.cpp | 9 ++++++--- test/object-store/schema.cpp | 9 +++++---- test/object-store/sync/flx_sync.cpp | 15 +++++++++++++++ 10 files changed, 46 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f074afed6dc..b3cc85fb864 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ * `Realm::refresh()` did not actually advance to the latest version in some cases. If there was a version newer than the current version which did not require blocking it would advance to that instead, contrary to the documented behavior. * Fixed `realm_query_parse_for_results` ignoring query for `query_result_t` passed as parameter ([#5841](https://github.com/realm/realm-core/pull/5841)). * Fixed `realm_query_parse_for_list` ignoring existing query ([#5850](https://github.com/realm/realm-core/pull/5850)). +* Fixed not allowing asymmetric tables in partition based sync ([#5691](https://github.com/realm/realm-core/issues/5691)). ### Breaking changes * None. diff --git a/src/realm.h b/src/realm.h index 2bff945cdfd..86585fa37fe 100644 --- a/src/realm.h +++ b/src/realm.h @@ -122,8 +122,9 @@ typedef enum realm_value_type { typedef enum realm_schema_validation_mode { RLM_SCHEMA_VALIDATION_BASIC = 0, - RLM_SCHEMA_VALIDATION_SYNC = 1, - RLM_SCHEMA_VALIDATION_REJECT_EMBEDDED_ORPHANS = 2 + RLM_SCHEMA_VALIDATION_SYNC_PBS = 1, + RLM_SCHEMA_VALIDATION_REJECT_EMBEDDED_ORPHANS = 2, + RLM_SCHEMA_VALIDATION_SYNC_FLX = 4 } realm_schema_validation_mode_e; /** diff --git a/src/realm/object-store/c_api/schema.cpp b/src/realm/object-store/c_api/schema.cpp index efa4fbfca72..6c80dc5fca5 100644 --- a/src/realm/object-store/c_api/schema.cpp +++ b/src/realm/object-store/c_api/schema.cpp @@ -58,7 +58,7 @@ RLM_API uint64_t realm_get_schema_version(const realm_t* realm) RLM_API bool realm_schema_validate(const realm_schema_t* schema, uint64_t validation_mode) { return wrap_err([&]() { - schema->ptr->validate(validation_mode); + schema->ptr->validate(static_cast(validation_mode)); return true; }); } diff --git a/src/realm/object-store/object_schema.cpp b/src/realm/object-store/object_schema.cpp index 15909958015..512f47eb521 100644 --- a/src/realm/object-store/object_schema.cpp +++ b/src/realm/object-store/object_schema.cpp @@ -329,7 +329,7 @@ static void validate_property(Schema const& schema, ObjectSchema const& parent_o } void ObjectSchema::validate(Schema const& schema, std::vector& exceptions, - bool for_sync) const + SchemaValidationMode validation_mode) const { std::vector public_property_names; std::vector internal_property_names; @@ -416,6 +416,8 @@ void ObjectSchema::validate(Schema const& schema, std::vector& exceptions, - bool for_sync) const; + SchemaValidationMode validation_mode) const; friend bool operator==(ObjectSchema const& a, ObjectSchema const& b) noexcept; diff --git a/src/realm/object-store/schema.cpp b/src/realm/object-store/schema.cpp index ec5c37e47c4..eff678472d6 100644 --- a/src/realm/object-store/schema.cpp +++ b/src/realm/object-store/schema.cpp @@ -190,7 +190,7 @@ std::unordered_set get_embedded_object_orphans(const Schema& schema } // end anonymous namespace -void Schema::validate(uint64_t validation_mode) const +void Schema::validate(SchemaValidationMode validation_mode) const { std::vector exceptions; @@ -206,9 +206,8 @@ void Schema::validate(uint64_t validation_mode) const ObjectSchemaValidationException("Type '%1' appears more than once in the schema.", it->name)); } - const bool for_sync = validation_mode & SchemaValidationMode::Sync; for (auto const& object : *this) { - object.validate(*this, exceptions, for_sync); + object.validate(*this, exceptions, validation_mode); } // TODO: remove this client side check once the server supports it diff --git a/src/realm/object-store/schema.hpp b/src/realm/object-store/schema.hpp index e73d3de5be5..44522882eeb 100644 --- a/src/realm/object-store/schema.hpp +++ b/src/realm/object-store/schema.hpp @@ -31,7 +31,7 @@ class StringData; struct TableKey; struct Property; -enum SchemaValidationMode : uint64_t { Basic = 0, Sync = 1, RejectEmbeddedOrphans = 2 }; +enum SchemaValidationMode : uint64_t { Basic = 0, SyncPBS = 1, RejectEmbeddedOrphans = 2, SyncFLX = 4 }; // How to handle update_schema() being called on a file which has // already been initialized with a different schema @@ -147,7 +147,7 @@ class Schema : private std::vector { // Verify that this schema is internally consistent (i.e. all properties are // valid, links link to types that actually exist, etc.) - void validate(uint64_t validation_mode = SchemaValidationMode::Basic) const; + void validate(SchemaValidationMode validation_mode = SchemaValidationMode::Basic) const; // Get the changes which must be applied to this schema to produce the passed-in schema std::vector compare(Schema const&, SchemaMode = SchemaMode::Automatic, diff --git a/src/realm/object-store/shared_realm.cpp b/src/realm/object-store/shared_realm.cpp index d700f0de382..736bfe96a27 100644 --- a/src/realm/object-store/shared_realm.cpp +++ b/src/realm/object-store/shared_realm.cpp @@ -385,14 +385,17 @@ void Realm::update_schema(Schema schema, uint64_t version, MigrationFunction mig DataInitializationFunction initialization_function, bool in_transaction) { uint64_t validation_mode = SchemaValidationMode::Basic; - if (m_config.sync_config) { - validation_mode |= SchemaValidationMode::Sync; +#if REALM_ENABLE_SYNC + if (auto sync_config = m_config.sync_config) { + validation_mode |= + sync_config->flx_sync_requested ? SchemaValidationMode::SyncFLX : SchemaValidationMode::SyncPBS; } +#endif if (m_config.schema_mode == SchemaMode::AdditiveExplicit) { validation_mode |= SchemaValidationMode::RejectEmbeddedOrphans; } - schema.validate(validation_mode); + schema.validate(static_cast(validation_mode)); bool was_in_read_transaction = is_in_read_transaction(); Schema actual_schema = get_full_schema(); diff --git a/test/object-store/schema.cpp b/test/object-store/schema.cpp index 33846e3920e..58268e213d4 100644 --- a/test/object-store/schema.cpp +++ b/test/object-store/schema.cpp @@ -372,7 +372,8 @@ TEST_CASE("Schema") { ObjectSchema::ObjectType::TopLevelAsymmetric, {{"_id", PropertyType::Int, Property::IsPrimary{true}}, {"street", PropertyType::String}}}, }; - REQUIRE_NOTHROW(schema.validate(SchemaValidationMode::Sync)); + REQUIRE_NOTHROW(schema.validate(SchemaValidationMode::SyncFLX)); + REQUIRE_THROWS(schema.validate(SchemaValidationMode::SyncPBS)); } SECTION("asymmetric tables not allowed in local realm") { @@ -392,7 +393,7 @@ TEST_CASE("Schema") { {{"_id", PropertyType::Int, Property::IsPrimary{true}}}}, }; REQUIRE_THROWS_CONTAINING( - schema.validate(SchemaValidationMode::Sync), + schema.validate(SchemaValidationMode::SyncFLX), "Property 'object.link' of type 'object' cannot be a link to an asymmetric object."); } @@ -403,7 +404,7 @@ TEST_CASE("Schema") { {{"link", PropertyType::Object | PropertyType::Nullable, "link target"}}}, {"link target", {{"value", PropertyType::Int}}}, }; - REQUIRE_THROWS_CONTAINING(schema.validate(SchemaValidationMode::Sync), + REQUIRE_THROWS_CONTAINING(schema.validate(SchemaValidationMode::SyncFLX), "Asymmetric table with property 'object.link' of type 'object' cannot have a " "non-embedded object type."); } @@ -416,7 +417,7 @@ TEST_CASE("Schema") { {"link", PropertyType::Object | PropertyType::Nullable, "link target"}}}, {"link target", ObjectSchema::ObjectType::Embedded, {{"value", PropertyType::Int}}}, }; - schema.validate(SchemaValidationMode::Sync); + schema.validate(SchemaValidationMode::SyncFLX); } SECTION("rejects array properties with no target object") { diff --git a/test/object-store/sync/flx_sync.cpp b/test/object-store/sync/flx_sync.cpp index 4fa4432ce23..6caba2d9693 100644 --- a/test/object-store/sync/flx_sync.cpp +++ b/test/object-store/sync/flx_sync.cpp @@ -1950,6 +1950,21 @@ TEST_CASE("flx: asymmetric sync", "[sync][flx][app]") { }); } + SECTION("asymmetric table not allowed in PBS") { + Schema schema{ + {"Asymmetric2", + ObjectSchema::ObjectType::TopLevelAsymmetric, + { + {"_id", PropertyType::Int, Property::IsPrimary{true}}, + {"location", PropertyType::Int}, + {"reading", PropertyType::Int}, + }}, + }; + + SyncTestFile config(harness->app(), bson::Bson{}, schema); + REQUIRE_THROWS(Realm::get_shared_realm(config)); + } + // Add any new test sections above this point SECTION("teardown") { From 4d21f9a87fdad3e6948fdb54cc56b3a743797cf3 Mon Sep 17 00:00:00 2001 From: nicola cabiddu Date: Wed, 14 Sep 2022 20:51:15 +0100 Subject: [PATCH 06/14] Expose `list_find` in the c api (#5848) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * expose list_find in the c api * changelog entry * fix compilation warning * Support finding a typed link in LnkLst * allow type_TypedLink for sets * Fix * added some more testing for element not found * more testing Co-authored-by: Jørgen Edelbo --- CHANGELOG.md | 1 + src/realm.h | 9 ++++ src/realm/list.hpp | 6 +++ src/realm/object-store/c_api/list.cpp | 21 +++++++++ src/realm/set.hpp | 17 +++++--- test/object-store/c_api/c_api.cpp | 61 ++++++++++++++++++++++++++- test/test_set.cpp | 9 ++-- 7 files changed, 114 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3cc85fb864..a6ffabe571e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * (PR [#????](https://github.com/realm/realm-core/pull/????)) * Convert object_store::Collection types into Results (PR [#5845](https://github.com/realm/realm-core/pull/5845)) * Expose `realm_object_get_parent` in the C API (PR [#5851](https://github.com/realm/realm-core/pull/5851)) +* Expose `realm_list_find` in the C API (PR [#5848](https://github.com/realm/realm-core/pull/5848)) ### Fixed * ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?) diff --git a/src/realm.h b/src/realm.h index 86585fa37fe..2992c98b7c8 100644 --- a/src/realm.h +++ b/src/realm.h @@ -1703,6 +1703,15 @@ RLM_API bool realm_list_get_property(const realm_list_t*, realm_property_info_t* */ RLM_API bool realm_list_get(const realm_list_t*, size_t index, realm_value_t* out_value); +/** + * Find the value in the list passed as parameter. + * @param value to search in the list + * @param out_index the index in the list where the value has been found or realm::not_found. + * @param out_found boolean that indicates whether the value is found or not + * @return true if no exception occurred. + */ +RLM_API bool realm_list_find(const realm_list_t*, const realm_value_t* value, size_t* out_index, bool* out_found); + /** * Set the value at @a index. * diff --git a/src/realm/list.hpp b/src/realm/list.hpp index f571be402a1..70e4583587f 100644 --- a/src/realm/list.hpp +++ b/src/realm/list.hpp @@ -1047,6 +1047,12 @@ inline size_t LnkLst::find_any(Mixed value) const else if (value.get_type() == type_Link) { return find_first(value.get()); } + else if (value.get_type() == type_TypedLink) { + auto link = value.get_link(); + if (link.get_table_key() == get_target_table()->get_key()) { + return find_first(link.get_obj_key()); + } + } return realm::not_found; } diff --git a/src/realm/object-store/c_api/list.cpp b/src/realm/object-store/c_api/list.cpp index ca26a0d7c3b..49a8fcf27cf 100644 --- a/src/realm/object-store/c_api/list.cpp +++ b/src/realm/object-store/c_api/list.cpp @@ -51,6 +51,27 @@ RLM_API bool realm_list_get(const realm_list_t* list, size_t index, realm_value_ }); } +RLM_API bool realm_list_find(const realm_list_t* list, const realm_value_t* value, size_t* out_index, bool* out_found) +{ + if (out_index) + *out_index = realm::not_found; + if (out_found) + *out_found = false; + + return wrap_err([&] { + list->verify_attached(); + auto val = from_capi(*value); + check_value_assignable(*list, val); + auto index = list->find_any(val); + if (out_index) + *out_index = index; + if (out_found) + *out_found = index < list->size(); + return true; + }); +} + + RLM_API bool realm_list_insert(realm_list_t* list, size_t index, realm_value_t value) { return wrap_err([&]() { diff --git a/src/realm/set.hpp b/src/realm/set.hpp index c426f35378c..93e1cb1b787 100644 --- a/src/realm/set.hpp +++ b/src/realm/set.hpp @@ -1240,13 +1240,18 @@ inline size_t LnkSet::find_any(Mixed value) const { if (value.is_null()) return not_found; - if (value.get_type() != type_Link) - return not_found; - size_t found = find(value.get()); - if (found != not_found) { - found = real2virtual(found); + + const auto type = value.get_type(); + if (type == type_Link) { + return find(value.get()); + } + if (type == type_TypedLink) { + auto link = value.get_link(); + if (link.get_table_key() == get_target_table()->get_key()) { + return find(link.get_obj_key()); + } } - return found; + return not_found; } inline bool LnkSet::is_obj_valid(size_t) const noexcept diff --git a/test/object-store/c_api/c_api.cpp b/test/object-store/c_api/c_api.cpp index 36a472aa7fb..32776ef2a2e 100644 --- a/test/object-store/c_api/c_api.cpp +++ b/test/object-store/c_api/c_api.cpp @@ -2460,6 +2460,23 @@ TEST_CASE("C API", "[c_api]") { CHECK(rlm_stdstr(a2) == "a"); CHECK(rlm_stdstr(b2) == "b"); CHECK(c2.type == RLM_TYPE_NULL); + + size_t out_index = -1; + bool found; + CHECK(checked(realm_list_find(strings.get(), &a2, &out_index, &found))); + CHECK(out_index == 0); + CHECK(found); + CHECK(checked(realm_list_find(strings.get(), &b2, &out_index, &found))); + CHECK(out_index == 1); + CHECK(found); + CHECK(checked(realm_list_find(strings.get(), &c2, &out_index, &found))); + CHECK(out_index == 2); + CHECK(found); + + realm_value_t dummy = rlm_str_val("c"); + CHECK(checked(realm_list_find(strings.get(), &dummy, &out_index, &found))); + CHECK(!found); + CHECK(out_index == realm::not_found); }); } @@ -2558,49 +2575,77 @@ TEST_CASE("C API", "[c_api]") { CHECK(realm_list_insert(nullable_uuid_list.get(), 1, null)); }); - realm_value_t value; + auto find = ([&](auto* list, auto* value) { + std::size_t index = -1; + bool found = false; + CHECK(checked(realm_list_find(list, value, &index, &found))); + CHECK(index == 0); + CHECK(found); + return (index < list->size()) && found == true; + }); + realm_value_t value; CHECK(realm_list_get(int_list.get(), 0, &value)); CHECK(rlm_val_eq(value, integer)); CHECK(!realm_list_get_linked_object(int_list.get(), 0)); + CHECK(find(int_list.get(), &value)); CHECK(realm_list_get(bool_list.get(), 0, &value)); CHECK(rlm_val_eq(value, boolean)); + CHECK(find(bool_list.get(), &value)); CHECK(realm_list_get(string_list.get(), 0, &value)); CHECK(rlm_val_eq(value, string)); + CHECK(find(string_list.get(), &value)); CHECK(realm_list_get(binary_list.get(), 0, &value)); CHECK(rlm_val_eq(value, binary)); + CHECK(find(binary_list.get(), &value)); CHECK(realm_list_get(timestamp_list.get(), 0, &value)); CHECK(rlm_val_eq(value, timestamp)); + CHECK(find(timestamp_list.get(), &value)); CHECK(realm_list_get(float_list.get(), 0, &value)); CHECK(rlm_val_eq(value, fnum)); + CHECK(find(float_list.get(), &value)); CHECK(realm_list_get(double_list.get(), 0, &value)); CHECK(rlm_val_eq(value, dnum)); + CHECK(find(double_list.get(), &value)); CHECK(realm_list_get(decimal_list.get(), 0, &value)); CHECK(rlm_val_eq(value, decimal)); + CHECK(find(decimal_list.get(), &value)); CHECK(realm_list_get(object_id_list.get(), 0, &value)); CHECK(rlm_val_eq(value, object_id)); + CHECK(find(object_id_list.get(), &value)); CHECK(realm_list_get(uuid_list.get(), 0, &value)); CHECK(rlm_val_eq(value, uuid)); + CHECK(find(uuid_list.get(), &value)); CHECK(realm_list_get(nullable_int_list.get(), 0, &value)); CHECK(rlm_val_eq(value, integer)); + CHECK(find(nullable_int_list.get(), &value)); CHECK(realm_list_get(nullable_bool_list.get(), 0, &value)); CHECK(rlm_val_eq(value, boolean)); + CHECK(find(nullable_bool_list.get(), &value)); CHECK(realm_list_get(nullable_string_list.get(), 0, &value)); CHECK(rlm_val_eq(value, string)); + CHECK(find(nullable_string_list.get(), &value)); CHECK(realm_list_get(nullable_binary_list.get(), 0, &value)); CHECK(rlm_val_eq(value, binary)); + CHECK(find(nullable_binary_list.get(), &value)); CHECK(realm_list_get(nullable_timestamp_list.get(), 0, &value)); CHECK(rlm_val_eq(value, timestamp)); + CHECK(find(nullable_timestamp_list.get(), &value)); CHECK(realm_list_get(nullable_float_list.get(), 0, &value)); CHECK(rlm_val_eq(value, fnum)); + CHECK(find(nullable_float_list.get(), &value)); CHECK(realm_list_get(nullable_double_list.get(), 0, &value)); CHECK(rlm_val_eq(value, dnum)); + CHECK(find(nullable_double_list.get(), &value)); CHECK(realm_list_get(nullable_decimal_list.get(), 0, &value)); CHECK(rlm_val_eq(value, decimal)); + CHECK(find(nullable_decimal_list.get(), &value)); CHECK(realm_list_get(nullable_object_id_list.get(), 0, &value)); CHECK(rlm_val_eq(value, object_id)); + CHECK(find(nullable_object_id_list.get(), &value)); CHECK(realm_list_get(nullable_uuid_list.get(), 0, &value)); CHECK(rlm_val_eq(value, uuid)); + CHECK(find(nullable_uuid_list.get(), &value)); write([&]() { CHECK(realm_list_insert(nullable_int_list.get(), 0, null)); @@ -2651,6 +2696,20 @@ TEST_CASE("C API", "[c_api]") { size_t size; CHECK(checked(realm_list_size(bars.get(), &size))); CHECK(size == 2); + + bool found = true; + size_t index = -1; + CHECK(checked(realm_list_find(bars.get(), &bar_link_val, &index, &found))); + CHECK(index == 0); + CHECK(found); + + realm_list_clear(bars.get()); + CHECK(checked(realm_list_find(bars.get(), &bar_link_val, &index, &found))); + CHECK(index == realm::not_found); + CHECK(!found); + + CHECK(checked(realm_list_insert(bars.get(), 0, bar_link_val))); + CHECK(checked(realm_list_insert(bars.get(), 1, bar_link_val))); }); SECTION("get") { diff --git a/test/test_set.cpp b/test/test_set.cpp index a2d3c4c0f5b..93e0f48ae31 100644 --- a/test/test_set.cpp +++ b/test/test_set.cpp @@ -268,8 +268,9 @@ TEST(Set_Links) CHECK_EQUAL(set_mixeds.find(bar3.get_link()), realm::npos); bar1.remove(); + set_links.insert(bar4.get_key()); - CHECK_EQUAL(set_links.size(), 2); + CHECK_EQUAL(set_links.size(), 3); CHECK_EQUAL(set_typed_links.size(), 3); CHECK_EQUAL(set_mixeds.size(), 3); @@ -281,14 +282,16 @@ TEST(Set_Links) auto bar2_link = bar2.get_link(); bar2.invalidate(); - CHECK_EQUAL(set_links.size(), 2); - CHECK_EQUAL(lnkset_links->size(), 1); // Unresolved link was hidden from LnkSet + CHECK_EQUAL(set_links.size(), 3); + CHECK_EQUAL(lnkset_links->size(), 2); // Unresolved link was hidden from LnkSet CHECK_EQUAL(set_typed_links.size(), 3); CHECK_EQUAL(set_mixeds.size(), 3); CHECK_EQUAL(set_links.find(bar2_key), realm::npos); // The original bar2 key is no longer in the set CHECK_NOT_EQUAL(set_links.find(bar2.get_key()), realm::npos); // The unresolved bar2 key is in the set CHECK_EQUAL(lnkset_links->find_any(bar2.get_key()), realm::npos); // The unresolved bar2 key is hidden by LnkSet + CHECK_EQUAL(lnkset_links->find_any(bar3.get_key()), 0); + CHECK_EQUAL(lnkset_links->find_any(bar4.get_key()), 1); CHECK_EQUAL(set_typed_links.find(bar2_link), realm::npos); CHECK_EQUAL(set_mixeds.find(bar2_link), realm::npos); From 0c6be164fdd311059f9e8bb321307594cad7c983 Mon Sep 17 00:00:00 2001 From: nicola cabiddu Date: Wed, 14 Sep 2022 22:09:03 +0100 Subject: [PATCH 07/14] Disable auto refresh for old realm during migration (#5856) * disable auto refresh for old realm during migration * changelog entry * Update CHANGELOG.md Co-authored-by: Thomas Goyne --- CHANGELOG.md | 1 + src/realm/object-store/shared_realm.cpp | 2 ++ test/object-store/c_api/c_api.cpp | 1 + test/object-store/realm.cpp | 2 ++ 4 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6ffabe571e..1da17b96ce6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ * Fixed `realm_query_parse_for_results` ignoring query for `query_result_t` passed as parameter ([#5841](https://github.com/realm/realm-core/pull/5841)). * Fixed `realm_query_parse_for_list` ignoring existing query ([#5850](https://github.com/realm/realm-core/pull/5850)). * Fixed not allowing asymmetric tables in partition based sync ([#5691](https://github.com/realm/realm-core/issues/5691)). +* Disable auto refresh for old realm instance passed to migration callbacks. ([#5856](https://github.com/realm/realm-core/pull/5856)). ### Breaking changes * None. diff --git a/src/realm/object-store/shared_realm.cpp b/src/realm/object-store/shared_realm.cpp index 736bfe96a27..6044571a052 100644 --- a/src/realm/object-store/shared_realm.cpp +++ b/src/realm/object-store/shared_realm.cpp @@ -448,6 +448,8 @@ void Realm::update_schema(Schema schema, uint64_t version, MigrationFunction mig // Don't go through the normal codepath for opening a Realm because // we're using a mismatched config auto old_realm = std::make_shared(std::move(config), none, m_coordinator, MakeSharedTag{}); + // block autorefresh for the old realm + old_realm->m_auto_refresh = false; migration_function(old_realm, shared_from_this(), m_schema); }; diff --git a/test/object-store/c_api/c_api.cpp b/test/object-store/c_api/c_api.cpp index 32776ef2a2e..02bed852986 100644 --- a/test/object-store/c_api/c_api.cpp +++ b/test/object-store/c_api/c_api.cpp @@ -762,6 +762,7 @@ bool migrate_schema(void* userdata_p, realm_t* old, realm_t* new_, const realm_s static_cast(old); static_cast(new_); ++userdata->num_migrations; + REQUIRE_FALSE((*old)->auto_refresh()); return true; } diff --git a/test/object-store/realm.cpp b/test/object-store/realm.cpp index ac70a115fd2..f516a81c0d6 100644 --- a/test/object-store/realm.cpp +++ b/test/object-store/realm.cpp @@ -281,6 +281,7 @@ TEST_CASE("SharedRealm: get_shared_realm()") { bool migration_called = false; config.migration_function = [&](SharedRealm old_realm, SharedRealm new_realm, Schema&) { migration_called = true; + REQUIRE_FALSE(old_realm->auto_refresh()); REQUIRE(ObjectStore::table_for_object_type(old_realm->read_group(), "object")->get_column_count() == 1); REQUIRE(ObjectStore::table_for_object_type(new_realm->read_group(), "object")->get_column_count() == 2); }; @@ -297,6 +298,7 @@ TEST_CASE("SharedRealm: get_shared_realm()") { }; bool migration_called = false; config.migration_function = [&](SharedRealm old_realm, SharedRealm new_realm, Schema&) { + REQUIRE_FALSE(old_realm->auto_refresh()); REQUIRE(ObjectStore::table_for_object_type(old_realm->read_group(), "object")->get_column_count() == 1); REQUIRE(ObjectStore::table_for_object_type(new_realm->read_group(), "object")->get_column_count() == 2); if (!migration_called) { From 404a8c61ab1a1da8a051b8c3f0cf1318b610da21 Mon Sep 17 00:00:00 2001 From: nicola cabiddu Date: Thu, 15 Sep 2022 14:47:17 +0100 Subject: [PATCH 08/14] expose `Group::remove_table` in the C API (#5860) * expose Group::remove_table in the C API * changelog entry * tests for realm_remove_table --- CHANGELOG.md | 1 + src/realm.h | 8 +++ src/realm/object-store/c_api/realm.cpp | 24 +++++++- test/object-store/c_api/c_api.cpp | 83 ++++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1da17b96ce6..19417e281b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Convert object_store::Collection types into Results (PR [#5845](https://github.com/realm/realm-core/pull/5845)) * Expose `realm_object_get_parent` in the C API (PR [#5851](https://github.com/realm/realm-core/pull/5851)) * Expose `realm_list_find` in the C API (PR [#5848](https://github.com/realm/realm-core/pull/5848)) +* Expose `Group::remove_table` in the C API (PR [#5860](https://github.com/realm/realm-core/pull/5860)) ### Fixed * ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?) diff --git a/src/realm.h b/src/realm.h index 2992c98b7c8..3632324b701 100644 --- a/src/realm.h +++ b/src/realm.h @@ -1135,6 +1135,14 @@ RLM_API realm_t* realm_freeze(const realm_t*); */ RLM_API bool realm_compact(realm_t*, bool* did_compact); +/** + * Find and delete the table passed as parementer for the realm instance passed to this function. + * @param table_name for the table the user wants to delete + * @param table_deleted in order to indicate if the table was actually deleted from realm + * @return true if no error has occured, false otherwise + */ +RLM_API bool realm_remove_table(realm_t*, const char* table_name, bool* table_deleted); + /** * Create a new schema from classes and their properties. * diff --git a/src/realm/object-store/c_api/realm.cpp b/src/realm/object-store/c_api/realm.cpp index 5f5689ab309..89545bdc52e 100644 --- a/src/realm/object-store/c_api/realm.cpp +++ b/src/realm/object-store/c_api/realm.cpp @@ -263,11 +263,33 @@ RLM_API bool realm_compact(realm_t* realm, bool* did_compact) { return wrap_err([&]() { auto& p = **realm; - *did_compact = p.compact(); + if (did_compact) + *did_compact = p.compact(); return true; }); } +RLM_API bool realm_remove_table(realm_t* realm, const char* table_name, bool* table_deleted) +{ + if (table_deleted) + *table_deleted = false; + + return wrap_err([&] { + auto table = ObjectStore::table_for_object_type((*realm)->read_group(), table_name); + if (table) { + const auto& schema = (*realm)->schema(); + const auto& object_schema = schema.find(table_name); + if (object_schema != schema.end()) { + throw std::logic_error("Attempt to remove a table that is currently part of the schema"); + } + (*realm)->read_group().remove_table(table->get_key()); + *table_deleted = true; + } + return true; + ; + }); +} + RLM_API realm_t* realm_from_thread_safe_reference(realm_thread_safe_reference_t* tsr, realm_scheduler_t* scheduler) { return wrap_err([&]() { diff --git a/test/object-store/c_api/c_api.cpp b/test/object-store/c_api/c_api.cpp index 02bed852986..6383b151a72 100644 --- a/test/object-store/c_api/c_api.cpp +++ b/test/object-store/c_api/c_api.cpp @@ -766,6 +766,18 @@ bool migrate_schema(void* userdata_p, realm_t* old, realm_t* new_, const realm_s return true; } +bool migrate_schema_delete_old_table(void* userdata_p, realm_t* old, realm_t* new_, const realm_schema_t*) +{ + auto userdata = static_cast(userdata_p); + static_cast(old); + static_cast(new_); + ++userdata->num_migrations; + bool table_deleted = false; + CHECK(checked(realm_remove_table(new_, "Foo", &table_deleted))); + CHECK(table_deleted); + return table_deleted; +} + bool migrate_schema_rename_prop(void* userdata_p, realm_t* old, realm_t* new_, const realm_schema_t* schema) { auto userdata = static_cast(userdata_p); @@ -837,6 +849,70 @@ TEST_CASE("C API", "[c_api]") { CHECK(userdata.num_migrations == 1); } + SECTION("migrate schema and delete old table") { + TestFile test_file_3; + ConfigUserdata userdata; + + realm_config_set_migration_function(config.get(), migrate_schema_delete_old_table, &userdata, nullptr); + const realm_class_info_t foo_class[1] = {{ + "Foo", + "int", + 1, + 0, + RLM_INVALID_CLASS_KEY, + RLM_CLASS_NORMAL, + }}; + const realm_class_info_t bar_class[1] = {{ + "Bar", + "int", + 1, + 0, + RLM_INVALID_CLASS_KEY, + RLM_CLASS_NORMAL, + }}; + const realm_property_info_t properties[1] = { + { + "int", + "", + RLM_PROPERTY_TYPE_INT, + RLM_COLLECTION_TYPE_NONE, + "", + "", + RLM_INVALID_PROPERTY_KEY, + RLM_PROPERTY_INDEXED | RLM_PROPERTY_PRIMARY_KEY, + }, + }; + const realm_property_info_t* props[1] = {properties}; + auto schema = cptr(realm_schema_new(foo_class, 1, props)); + auto new_schema = cptr(realm_schema_new(bar_class, 1, props)); + CHECK(checked(schema.get())); + CHECK(checked(new_schema.get())); + REQUIRE(checked(realm_schema_validate(schema.get(), RLM_SCHEMA_VALIDATION_BASIC))); + REQUIRE(checked(realm_schema_validate(new_schema.get(), RLM_SCHEMA_VALIDATION_BASIC))); + // realm with schema containing Foo + auto config = cptr(realm_config_new()); + realm_config_set_path(config.get(), test_file_3.path.c_str()); + realm_config_set_schema_mode(config.get(), RLM_SCHEMA_MODE_AUTOMATIC); + realm_config_set_schema_version(config.get(), 0); + realm_config_set_schema(config.get(), schema.get()); + auto realm = cptr_checked(realm_open(config.get())); + CHECK(userdata.num_migrations == 0); + realm.reset(); + // migrate schema basically changing Foo into Bar + auto config2 = cptr(realm_config_new()); + realm_config_set_path(config2.get(), test_file_3.path.c_str()); + realm_config_set_schema_mode(config2.get(), RLM_SCHEMA_MODE_AUTOMATIC); + realm_config_set_schema_version(config2.get(), 999); + realm_config_set_schema(config2.get(), new_schema.get()); + realm_config_set_migration_function(config2.get(), migrate_schema_delete_old_table, &userdata, nullptr); + auto realm2 = cptr_checked(realm_open(config2.get())); + CHECK(userdata.num_migrations == 1); + auto new_db_schema = realm_get_schema(realm2.get()); + CHECK(realm_equals(new_db_schema, new_schema.get())); + realm2.reset(); + realm_release(new_db_schema); + } + SECTION("migration callback rename property") { TestFile test_file_3; ConfigUserdata userdata; @@ -1388,6 +1464,13 @@ TEST_CASE("C API", "[c_api]") { CHECK(did_compact); } + SECTION("realm_remove_table()") { + bool table_deleted = true; + CHECK(!realm_remove_table(realm, "Foo", &table_deleted)); + CHECK_ERR(RLM_ERR_LOGIC); + CHECK(!table_deleted); + } + SECTION("realm_get_class_keys()") { realm_class_key_t keys[2]; // return total number of keys present, copy only if there is enough space in the vector passed in From 8f49440429e08cb3edcaca28eb3294d5cb7e1e71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Edelbo?= Date: Thu, 15 Sep 2022 15:59:00 +0200 Subject: [PATCH 09/14] Encode links in a way the server can understand (#5835) Instead of encoding a link as O123 or L0:123, it should be coded like obj(, ) --- CHANGELOG.md | 1 + src/realm/parser/driver.cpp | 29 +- src/realm/parser/driver.hpp | 5 + src/realm/parser/generated/query_bison.cpp | 551 ++++----- src/realm/parser/generated/query_bison.hpp | 161 +-- src/realm/parser/generated/query_flex.cpp | 1202 ++++++++++---------- src/realm/parser/query_bison.yy | 21 +- src/realm/parser/query_flex.ll | 1 + src/realm/query.cpp | 6 +- src/realm/query_engine.hpp | 14 +- src/realm/query_expression.hpp | 48 +- src/realm/table.cpp | 1 - src/realm/util/serializer.cpp | 19 +- src/realm/util/serializer.hpp | 11 +- test/test_metrics.cpp | 2 +- test/test_parser.cpp | 109 +- 16 files changed, 1217 insertions(+), 964 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19417e281b4..5bb79c96d3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ * If a SyncSession outlived the parent Realm and then was adopted by a new Realm for the same file, other processes would not get notified for sync writes on that file. * Fix one cause of QoS inversion warnings when performing writes on the main thread on Apple platforms. Waiting for async notifications to be ready is now done in a QoS-aware ways. * `Realm::refresh()` did not actually advance to the latest version in some cases. If there was a version newer than the current version which did not require blocking it would advance to that instead, contrary to the documented behavior. +* If you set a subscription on a link in flexible sync, the server would not know how to handle it ([#5409](https://github.com/realm/realm-core/issues/5409)) * Fixed `realm_query_parse_for_results` ignoring query for `query_result_t` passed as parameter ([#5841](https://github.com/realm/realm-core/pull/5841)). * Fixed `realm_query_parse_for_list` ignoring existing query ([#5850](https://github.com/realm/realm-core/pull/5850)). * Fixed not allowing asymmetric tables in partition based sync ([#5691](https://github.com/realm/realm-core/issues/5691)). diff --git a/src/realm/parser/driver.cpp b/src/realm/parser/driver.cpp index 651901dab7f..eb47b12b7d1 100644 --- a/src/realm/parser/driver.cpp +++ b/src/realm/parser/driver.cpp @@ -373,7 +373,7 @@ std::unique_ptr OperationNode::visit(ParserDriver* drv, DataType type) } } if (!Mixed::is_numeric(left->get_type(), right->get_type())) { - util::serializer::SerialisationState state(""); + util::serializer::SerialisationState state("", nullptr); std::string op(&m_op, 1); throw std::invalid_argument(util::format("Cannot perform '%1' operation on '%2' and '%3'", op, left->description(state), right->description(state))); @@ -490,8 +490,9 @@ Query EqualityNode::visit(ParserDriver* drv) } else if (left_type == type_Link) { auto link_column = dynamic_cast*>(left.get()); - if (link_column && link_column->link_map().get_nb_hops() == 1 && link_column->get_comparison_type() && - *link_column->get_comparison_type() == ExpressionComparisonType::Any) { + if (link_column && link_column->link_map().get_nb_hops() == 1 && + link_column->get_comparison_type().value_or(ExpressionComparisonType::Any) == + ExpressionComparisonType::Any) { // We can use equal/not_equal and get a LinksToNode based query if (op == CompareNode::EQUAL) { return drv->m_base_table->where().equal(link_column->link_map().get_first_column_key(), val); @@ -939,6 +940,26 @@ std::unique_ptr ConstantNode::visit(ParserDriver* drv, DataType hint) { std::unique_ptr ret; std::string explain_value_message = text; + if (target_table.length()) { + const Group* g = drv->m_base_table->get_parent_group(); + TableKey table_key; + ObjKey obj_key; + auto table = g->get_table(target_table); + if (!table) { + // Perhaps class prefix is missing + Group::TableNameBuffer buffer; + table = g->get_table(Group::class_name_to_table_name(target_table, buffer)); + } + if (!table) { + throw InvalidQueryError(util::format("Unknown object type '%1'", target_table)); + } + table_key = table->get_key(); + target_table = ""; + auto pk_val_node = visit(drv, hint); // call recursively + auto pk_val = pk_val_node->get_mixed(); + obj_key = table->find_primary_key(pk_val); + return std::make_unique>(ObjLink(table_key, ObjKey(obj_key))); + } switch (type) { case Type::NUMBER: { if (hint == type_Decimal) { @@ -1403,7 +1424,7 @@ static void verify_conditions(Subexpr* left, Subexpr* right, util::serializer::S } ParserDriver::ParserDriver(TableRef t, Arguments& args, const query_parser::KeyPathMapping& mapping) - : m_serializer_state(mapping.get_backlink_class_prefix()) + : m_serializer_state(mapping.get_backlink_class_prefix(), nullptr) , m_base_table(t) , m_args(args) , m_mapping(mapping) diff --git a/src/realm/parser/driver.hpp b/src/realm/parser/driver.hpp index 5f57c693b63..d48e8a4c1dd 100644 --- a/src/realm/parser/driver.hpp +++ b/src/realm/parser/driver.hpp @@ -146,8 +146,13 @@ class ConstantNode : public ParserNode { , m_comp_type(comp_type) { } + void add_table(std::string table_name) + { + target_table = table_name.substr(1, table_name.size() - 2); + } std::unique_ptr visit(ParserDriver*, DataType); util::Optional m_comp_type; + std::string target_table; }; class ListNode : public ParserNode { diff --git a/src/realm/parser/generated/query_bison.cpp b/src/realm/parser/generated/query_bison.cpp index 87af555fba2..f873ffccb1f 100644 --- a/src/realm/parser/generated/query_bison.cpp +++ b/src/realm/parser/generated/query_bison.cpp @@ -199,6 +199,7 @@ namespace yy { break; case symbol_kind::SYM_constant: // constant + case symbol_kind::SYM_primary_key: // primary_key value.YY_MOVE_OR_COPY< ConstantNode* > (YY_MOVE (that.value)); break; @@ -287,6 +288,7 @@ namespace yy { case symbol_kind::SYM_LIKE: // "like" case symbol_kind::SYM_BETWEEN: // "between" case symbol_kind::SYM_IN: // "in" + case symbol_kind::SYM_OBJ: // "obj" case symbol_kind::SYM_SORT: // "sort" case symbol_kind::SYM_DISTINCT: // "distinct" case symbol_kind::SYM_LIMIT: // "limit" @@ -320,6 +322,7 @@ namespace yy { break; case symbol_kind::SYM_constant: // constant + case symbol_kind::SYM_primary_key: // primary_key value.move< ConstantNode* > (YY_MOVE (that.value)); break; @@ -408,6 +411,7 @@ namespace yy { case symbol_kind::SYM_LIKE: // "like" case symbol_kind::SYM_BETWEEN: // "between" case symbol_kind::SYM_IN: // "in" + case symbol_kind::SYM_OBJ: // "obj" case symbol_kind::SYM_SORT: // "sort" case symbol_kind::SYM_DISTINCT: // "distinct" case symbol_kind::SYM_LIMIT: // "limit" @@ -441,6 +445,7 @@ namespace yy { break; case symbol_kind::SYM_constant: // constant + case symbol_kind::SYM_primary_key: // primary_key value.copy< ConstantNode* > (that.value); break; @@ -529,6 +534,7 @@ namespace yy { case symbol_kind::SYM_LIKE: // "like" case symbol_kind::SYM_BETWEEN: // "between" case symbol_kind::SYM_IN: // "in" + case symbol_kind::SYM_OBJ: // "obj" case symbol_kind::SYM_SORT: // "sort" case symbol_kind::SYM_DISTINCT: // "distinct" case symbol_kind::SYM_LIMIT: // "limit" @@ -560,6 +566,7 @@ namespace yy { break; case symbol_kind::SYM_constant: // constant + case symbol_kind::SYM_primary_key: // primary_key value.move< ConstantNode* > (that.value); break; @@ -648,6 +655,7 @@ namespace yy { case symbol_kind::SYM_LIKE: // "like" case symbol_kind::SYM_BETWEEN: // "between" case symbol_kind::SYM_IN: // "in" + case symbol_kind::SYM_OBJ: // "obj" case symbol_kind::SYM_SORT: // "sort" case symbol_kind::SYM_DISTINCT: // "distinct" case symbol_kind::SYM_LIMIT: // "limit" @@ -875,6 +883,10 @@ namespace yy { { yyo << yysym.value.template as < std::string > (); } break; + case symbol_kind::SYM_OBJ: // "obj" + { yyo << yysym.value.template as < std::string > (); } + break; + case symbol_kind::SYM_SORT: // "sort" { yyo << yysym.value.template as < std::string > (); } break; @@ -907,51 +919,51 @@ namespace yy { { yyo << yysym.value.template as < std::string > (); } break; - case symbol_kind::SYM_55_: // '+' + case symbol_kind::SYM_56_: // '+' { yyo << "<>"; } break; - case symbol_kind::SYM_56_: // '-' + case symbol_kind::SYM_57_: // '-' { yyo << "<>"; } break; - case symbol_kind::SYM_57_: // '*' + case symbol_kind::SYM_58_: // '*' { yyo << "<>"; } break; - case symbol_kind::SYM_58_: // '/' + case symbol_kind::SYM_59_: // '/' { yyo << "<>"; } break; - case symbol_kind::SYM_59_: // '(' + case symbol_kind::SYM_60_: // '(' { yyo << "<>"; } break; - case symbol_kind::SYM_60_: // ')' + case symbol_kind::SYM_61_: // ')' { yyo << "<>"; } break; - case symbol_kind::SYM_61_: // '[' + case symbol_kind::SYM_62_: // '[' { yyo << "<>"; } break; - case symbol_kind::SYM_62_: // ']' + case symbol_kind::SYM_63_: // ']' { yyo << "<>"; } break; - case symbol_kind::SYM_63_: // '.' + case symbol_kind::SYM_64_: // '.' { yyo << "<>"; } break; - case symbol_kind::SYM_64_: // ',' + case symbol_kind::SYM_65_: // ',' { yyo << "<>"; } break; - case symbol_kind::SYM_65_: // '{' + case symbol_kind::SYM_66_: // '{' { yyo << "<>"; } break; - case symbol_kind::SYM_66_: // '}' + case symbol_kind::SYM_67_: // '}' { yyo << "<>"; } break; @@ -1027,6 +1039,10 @@ namespace yy { { yyo << yysym.value.template as < ConstantNode* > (); } break; + case symbol_kind::SYM_primary_key: // primary_key + { yyo << yysym.value.template as < ConstantNode* > (); } + break; + case symbol_kind::SYM_boolexpr: // boolexpr { yyo << yysym.value.template as < TrueOrFalseNode* > (); } break; @@ -1296,6 +1312,7 @@ namespace yy { break; case symbol_kind::SYM_constant: // constant + case symbol_kind::SYM_primary_key: // primary_key yylhs.value.emplace< ConstantNode* > (); break; @@ -1384,6 +1401,7 @@ namespace yy { case symbol_kind::SYM_LIKE: // "like" case symbol_kind::SYM_BETWEEN: // "between" case symbol_kind::SYM_IN: // "in" + case symbol_kind::SYM_OBJ: // "obj" case symbol_kind::SYM_SORT: // "sort" case symbol_kind::SYM_DISTINCT: // "distinct" case symbol_kind::SYM_LIMIT: // "limit" @@ -1615,231 +1633,243 @@ namespace yy { { yystack_[2].value.as < ListNode* > ()->add_element(yystack_[0].value.as < ConstantNode* > ()); yylhs.value.as < ListNode* > () = yystack_[2].value.as < ListNode* > (); } break; - case 51: // constant: "natural0" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::NUMBER, yystack_[0].value.as < std::string > ()); } - break; - - case 52: // constant: "number" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::NUMBER, yystack_[0].value.as < std::string > ()); } + case 51: // constant: primary_key + { yylhs.value.as < ConstantNode* > () = yystack_[0].value.as < ConstantNode* > (); } break; - case 53: // constant: "infinity" + case 52: // constant: "infinity" { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::INFINITY_VAL, yystack_[0].value.as < std::string > ()); } break; - case 54: // constant: "NaN" + case 53: // constant: "NaN" { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::NAN_VAL, yystack_[0].value.as < std::string > ()); } break; - case 55: // constant: "string" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::STRING, yystack_[0].value.as < std::string > ()); } - break; - - case 56: // constant: "base64" + case 54: // constant: "base64" { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::BASE64, yystack_[0].value.as < std::string > ()); } break; - case 57: // constant: "float" + case 55: // constant: "float" { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::FLOAT, yystack_[0].value.as < std::string > ()); } break; - case 58: // constant: "date" + case 56: // constant: "date" { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::TIMESTAMP, yystack_[0].value.as < std::string > ()); } break; - case 59: // constant: "UUID" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::UUID_T, yystack_[0].value.as < std::string > ()); } - break; - - case 60: // constant: "ObjectId" - { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::OID, yystack_[0].value.as < std::string > ()); } - break; - - case 61: // constant: "link" + case 57: // constant: "link" { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::LINK, yystack_[0].value.as < std::string > ()); } break; - case 62: // constant: "typed link" + case 58: // constant: "typed link" { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::TYPED_LINK, yystack_[0].value.as < std::string > ()); } break; - case 63: // constant: "true" + case 59: // constant: "true" { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::TRUE, ""); } break; - case 64: // constant: "false" + case 60: // constant: "false" { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::FALSE, ""); } break; - case 65: // constant: "null" + case 61: // constant: "null" { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::NULL_VAL, ""); } break; - case 66: // constant: "argument" + case 62: // constant: "argument" { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::ARG, yystack_[0].value.as < std::string > ()); } break; - case 67: // constant: comp_type "argument" + case 63: // constant: comp_type "argument" { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ExpressionComparisonType(yystack_[1].value.as < int > ()), yystack_[0].value.as < std::string > ()); } break; - case 68: // boolexpr: "truepredicate" + case 64: // constant: "obj" '(' "string" ',' primary_key ')' + { + auto tmp = yystack_[1].value.as < ConstantNode* > (); + tmp->add_table(yystack_[3].value.as < std::string > ()); + yylhs.value.as < ConstantNode* > () = tmp; + } + break; + + case 65: // primary_key: "natural0" + { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::NUMBER, yystack_[0].value.as < std::string > ()); } + break; + + case 66: // primary_key: "number" + { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::NUMBER, yystack_[0].value.as < std::string > ()); } + break; + + case 67: // primary_key: "string" + { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::STRING, yystack_[0].value.as < std::string > ()); } + break; + + case 68: // primary_key: "UUID" + { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::UUID_T, yystack_[0].value.as < std::string > ()); } + break; + + case 69: // primary_key: "ObjectId" + { yylhs.value.as < ConstantNode* > () = drv.m_parse_nodes.create(ConstantNode::OID, yystack_[0].value.as < std::string > ()); } + break; + + case 70: // boolexpr: "truepredicate" { yylhs.value.as < TrueOrFalseNode* > () = drv.m_parse_nodes.create(true); } break; - case 69: // boolexpr: "falsepredicate" + case 71: // boolexpr: "falsepredicate" { yylhs.value.as < TrueOrFalseNode* > () = drv.m_parse_nodes.create(false); } break; - case 70: // comp_type: "any" + case 72: // comp_type: "any" { yylhs.value.as < int > () = int(ExpressionComparisonType::Any); } break; - case 71: // comp_type: "all" + case 73: // comp_type: "all" { yylhs.value.as < int > () = int(ExpressionComparisonType::All); } break; - case 72: // comp_type: "none" + case 74: // comp_type: "none" { yylhs.value.as < int > () = int(ExpressionComparisonType::None); } break; - case 73: // post_op: %empty + case 75: // post_op: %empty { yylhs.value.as < PostOpNode* > () = nullptr; } break; - case 74: // post_op: '.' "@size" + case 76: // post_op: '.' "@size" { yylhs.value.as < PostOpNode* > () = drv.m_parse_nodes.create(yystack_[0].value.as < std::string > (), PostOpNode::SIZE);} break; - case 75: // post_op: '.' "@type" + case 77: // post_op: '.' "@type" { yylhs.value.as < PostOpNode* > () = drv.m_parse_nodes.create(yystack_[0].value.as < std::string > (), PostOpNode::TYPE);} break; - case 76: // aggr_op: "@max" + case 78: // aggr_op: "@max" { yylhs.value.as < AggrNode* > () = drv.m_parse_nodes.create(AggrNode::MAX);} break; - case 77: // aggr_op: "@min" + case 79: // aggr_op: "@min" { yylhs.value.as < AggrNode* > () = drv.m_parse_nodes.create(AggrNode::MIN);} break; - case 78: // aggr_op: "@sun" + case 80: // aggr_op: "@sun" { yylhs.value.as < AggrNode* > () = drv.m_parse_nodes.create(AggrNode::SUM);} break; - case 79: // aggr_op: "@average" + case 81: // aggr_op: "@average" { yylhs.value.as < AggrNode* > () = drv.m_parse_nodes.create(AggrNode::AVG);} break; - case 80: // equality: "==" + case 82: // equality: "==" { yylhs.value.as < int > () = CompareNode::EQUAL; } break; - case 81: // equality: "!=" + case 83: // equality: "!=" { yylhs.value.as < int > () = CompareNode::NOT_EQUAL; } break; - case 82: // equality: "in" + case 84: // equality: "in" { yylhs.value.as < int > () = CompareNode::IN; } break; - case 83: // relational: "<" + case 85: // relational: "<" { yylhs.value.as < int > () = CompareNode::LESS; } break; - case 84: // relational: "<=" + case 86: // relational: "<=" { yylhs.value.as < int > () = CompareNode::LESS_EQUAL; } break; - case 85: // relational: ">" + case 87: // relational: ">" { yylhs.value.as < int > () = CompareNode::GREATER; } break; - case 86: // relational: ">=" + case 88: // relational: ">=" { yylhs.value.as < int > () = CompareNode::GREATER_EQUAL; } break; - case 87: // stringop: "beginswith" + case 89: // stringop: "beginswith" { yylhs.value.as < int > () = CompareNode::BEGINSWITH; } break; - case 88: // stringop: "endswith" + case 90: // stringop: "endswith" { yylhs.value.as < int > () = CompareNode::ENDSWITH; } break; - case 89: // stringop: "contains" + case 91: // stringop: "contains" { yylhs.value.as < int > () = CompareNode::CONTAINS; } break; - case 90: // stringop: "like" + case 92: // stringop: "like" { yylhs.value.as < int > () = CompareNode::LIKE; } break; - case 91: // path: %empty + case 93: // path: %empty { yylhs.value.as < PathNode* > () = drv.m_parse_nodes.create(); } break; - case 92: // path: path path_elem + case 94: // path: path path_elem { yystack_[1].value.as < PathNode* > ()->add_element(yystack_[0].value.as < std::string > ()); yylhs.value.as < PathNode* > () = yystack_[1].value.as < PathNode* > (); } break; - case 93: // path_elem: id '.' + case 95: // path_elem: id '.' { yylhs.value.as < std::string > () = yystack_[1].value.as < std::string > (); } break; - case 94: // id: "identifier" + case 96: // id: "identifier" { yylhs.value.as < std::string > () = yystack_[0].value.as < std::string > (); } break; - case 95: // id: "@links" '.' "identifier" '.' "identifier" + case 97: // id: "@links" '.' "identifier" '.' "identifier" { yylhs.value.as < std::string > () = std::string("@links.") + yystack_[2].value.as < std::string > () + "." + yystack_[0].value.as < std::string > (); } break; - case 96: // id: "beginswith" + case 98: // id: "beginswith" { yylhs.value.as < std::string > () = yystack_[0].value.as < std::string > (); } break; - case 97: // id: "endswith" + case 99: // id: "endswith" { yylhs.value.as < std::string > () = yystack_[0].value.as < std::string > (); } break; - case 98: // id: "contains" + case 100: // id: "contains" { yylhs.value.as < std::string > () = yystack_[0].value.as < std::string > (); } break; - case 99: // id: "like" + case 101: // id: "like" { yylhs.value.as < std::string > () = yystack_[0].value.as < std::string > (); } break; - case 100: // id: "between" + case 102: // id: "between" { yylhs.value.as < std::string > () = yystack_[0].value.as < std::string > (); } break; - case 101: // id: "key or value" + case 103: // id: "key or value" { yylhs.value.as < std::string > () = yystack_[0].value.as < std::string > (); } break; - case 102: // id: "sort" + case 104: // id: "sort" { yylhs.value.as < std::string > () = yystack_[0].value.as < std::string > (); } break; - case 103: // id: "distinct" + case 105: // id: "distinct" { yylhs.value.as < std::string > () = yystack_[0].value.as < std::string > (); } break; - case 104: // id: "limit" + case 106: // id: "limit" { yylhs.value.as < std::string > () = yystack_[0].value.as < std::string > (); } break; - case 105: // id: "ascending" + case 107: // id: "ascending" { yylhs.value.as < std::string > () = yystack_[0].value.as < std::string > (); } break; - case 106: // id: "descending" + case 108: // id: "descending" { yylhs.value.as < std::string > () = yystack_[0].value.as < std::string > (); } break; - case 107: // id: "in" + case 109: // id: "in" { yylhs.value.as < std::string > () = yystack_[0].value.as < std::string > (); } break; @@ -2191,166 +2221,174 @@ namespace yy { } - const signed char parser::yypact_ninf_ = -81; + const signed char parser::yypact_ninf_ = -83; const signed char parser::yytable_ninf_ = -1; const short parser::yypact_[] = { - 130, -81, -81, -39, -81, -81, -81, -81, -81, -81, - 130, -81, -81, -81, -81, -81, -81, -81, -81, -81, - -81, -81, -81, -81, 130, 389, 27, -13, -81, 84, - 136, -81, -81, -81, -81, -81, 5, 307, -81, -81, - -16, 19, -10, -81, 2, -81, 130, 130, 36, -81, - -81, -81, -81, -81, -81, -81, 206, 206, 206, 206, - 168, 206, -81, -81, -81, -81, 23, 244, -81, 389, - 340, 15, -81, -81, -81, -81, -81, -81, -81, -81, - -81, -81, -81, -81, -81, -81, 3, -14, 340, -81, - -81, 389, -81, -81, 29, 4, 22, 28, -81, -81, - -81, 206, -23, -81, -23, -81, -81, 206, 58, 58, - -81, 41, 282, -81, 39, 44, 46, 16, -81, 389, - 97, -81, 340, 47, -81, -81, -81, 79, 71, 58, - -81, -81, 94, 37, -81, 60, -81, -81, 62, -81, - -81, -81, -81, 69, 80, -81, -42, 340, -41, 340, - 83, 124, 89, 340, 130, -81, -81, -37, -81, -81, - 47, -81, -81, 37, -81, -81, -8, 340, -81, -81, - -81, 340, 90, -37, 47, 103, -81, -81 + 136, -83, -83, -24, -83, -83, -83, -83, -83, -83, + 136, -83, -83, -83, -83, -83, -83, -83, -83, -83, + -83, -83, -83, -83, -13, 136, 358, 24, -12, -83, + 194, 76, -83, -83, -83, -83, -83, -83, -29, 380, + -83, -83, 11, -16, 21, 19, -83, 14, -83, 136, + 136, 23, -83, -83, -83, -83, -83, -83, -83, 227, + 227, 227, 227, 182, 227, -83, -83, -83, -83, 2, + 273, -83, 358, 400, -6, -83, -83, -83, -83, -83, + -83, -83, -83, -83, -83, -83, -83, -83, -83, 64, + 4, 400, 16, -83, -83, 358, -83, -83, 31, 6, + 47, 51, -83, -83, -83, 227, -30, -83, -30, -83, + -83, 227, 75, 75, -83, 49, 316, -83, 71, 36, + 58, -10, -83, 358, 70, -83, 400, 61, 77, -83, + -83, -83, 97, 40, 75, -83, -83, 108, 34, -83, + 73, -83, -83, 82, -83, -83, -83, -83, 83, 81, + -83, 87, -19, 400, -8, 400, 88, 123, 91, 400, + 136, -83, -83, -83, -37, -83, -83, 61, -83, -83, + 34, -83, -83, -2, 400, -83, -83, -83, 400, 92, + -37, 61, 104, -83, -83 }; const signed char parser::yydefact_[] = { - 91, 68, 69, 0, 63, 64, 65, 70, 71, 72, - 91, 55, 56, 53, 54, 51, 52, 57, 58, 59, - 60, 61, 62, 66, 91, 49, 0, 33, 3, 0, - 15, 22, 30, 23, 21, 8, 91, 0, 91, 6, - 0, 0, 0, 48, 0, 1, 91, 91, 2, 80, - 81, 83, 85, 86, 84, 82, 91, 91, 91, 91, - 91, 91, 87, 88, 89, 90, 0, 91, 67, 49, - 0, 73, 94, 96, 97, 98, 99, 100, 107, 102, - 103, 104, 105, 106, 101, 92, 73, 0, 0, 7, - 16, 0, 46, 5, 4, 0, 0, 0, 35, 34, - 36, 91, 19, 15, 20, 17, 18, 91, 9, 11, - 14, 0, 91, 12, 0, 0, 73, 0, 27, 0, - 93, 24, 0, 31, 50, 91, 91, 0, 0, 10, - 13, 47, 0, 93, 26, 0, 74, 75, 0, 76, - 77, 78, 79, 29, 0, 93, 0, 0, 0, 0, - 0, 0, 73, 0, 91, 40, 91, 0, 37, 91, - 38, 43, 95, 0, 25, 28, 0, 0, 44, 45, - 41, 0, 0, 0, 39, 0, 42, 32 + 93, 70, 71, 0, 59, 60, 61, 72, 73, 74, + 93, 67, 54, 52, 53, 65, 66, 55, 56, 68, + 69, 57, 58, 62, 0, 93, 49, 0, 33, 3, + 0, 15, 22, 30, 23, 21, 51, 8, 93, 0, + 93, 6, 0, 0, 0, 0, 48, 0, 1, 93, + 93, 2, 82, 83, 85, 87, 88, 86, 84, 93, + 93, 93, 93, 93, 93, 89, 90, 91, 92, 0, + 93, 63, 49, 0, 75, 96, 98, 99, 100, 101, + 102, 109, 104, 105, 106, 107, 108, 103, 94, 75, + 0, 0, 0, 7, 16, 0, 46, 5, 4, 0, + 0, 0, 35, 34, 36, 93, 19, 15, 20, 17, + 18, 93, 9, 11, 14, 0, 93, 12, 0, 0, + 75, 0, 27, 0, 95, 24, 0, 31, 0, 50, + 93, 93, 0, 0, 10, 13, 47, 0, 95, 26, + 0, 76, 77, 0, 78, 79, 80, 81, 29, 0, + 95, 0, 0, 0, 0, 0, 0, 0, 75, 0, + 93, 64, 40, 93, 0, 37, 93, 38, 43, 97, + 0, 25, 28, 0, 0, 44, 45, 41, 0, 0, + 0, 39, 0, 42, 32 }; const signed char parser::yypgoto_[] = { - -81, -81, -9, -81, 1, 0, -81, -81, -81, -81, - -81, -81, -81, -81, -81, -2, 91, 113, -20, -81, - -18, -80, -81, -81, -81, -81, -34, -81, -67 + -83, -83, -9, -83, 1, 0, -83, -83, -83, -83, + -83, -83, -83, -83, -83, -22, 90, 89, -20, 35, + -83, -21, -82, -83, -83, -83, -83, -36, -83, -70 }; const unsigned char parser::yydefgoto_[] = { - 0, 26, 27, 28, 29, 103, 31, 87, 32, 48, - 98, 148, 99, 146, 100, 170, 33, 42, 34, 35, - 36, 118, 143, 60, 61, 67, 37, 85, 86 + 0, 27, 28, 29, 30, 107, 32, 90, 33, 51, + 102, 154, 103, 152, 104, 177, 34, 45, 35, 36, + 37, 38, 122, 148, 63, 64, 70, 39, 88, 89 }; const unsigned char parser::yytable_[] = { - 30, 39, 70, 116, 88, 43, 121, 44, 46, 47, - 30, 46, 47, 168, 169, 40, 46, 47, 155, 158, - 38, 123, 156, 159, 30, 41, 145, 45, 49, 50, - 51, 52, 53, 54, 58, 59, 134, 93, 94, 7, - 8, 9, 68, 135, 89, 68, 30, 30, 111, 43, - 122, 44, 172, 46, 91, 144, 92, 102, 104, 105, - 106, 108, 109, 125, 119, 55, 120, 113, 136, 137, - 69, 124, 164, 44, 56, 57, 58, 59, 117, 90, - 157, 126, 160, 95, 96, 97, 165, 127, 25, 136, - 137, 147, 149, 49, 50, 51, 52, 53, 54, 138, - 173, 44, 128, 91, 174, 131, 69, 132, 129, 133, - 145, 150, 130, 56, 57, 58, 59, 139, 140, 141, - 142, 135, 167, 151, 152, 171, 56, 57, 58, 59, - 55, 90, 153, 1, 2, 3, 4, 5, 6, 56, - 57, 58, 59, 161, 154, 166, 7, 8, 9, 136, - 137, 162, 163, 175, 30, 177, 10, 110, 11, 12, - 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, - 23, 176, 0, 3, 4, 5, 6, 62, 63, 64, - 65, 66, 114, 107, 7, 8, 9, 0, 0, 24, - 0, 0, 0, 0, 0, 25, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 0, - 0, 3, 4, 5, 6, 0, 0, 0, 0, 0, - 0, 0, 7, 8, 9, 0, 0, 101, 0, 0, - 0, 0, 0, 25, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 0, 0, 3, - 4, 5, 6, 0, 0, 0, 0, 0, 0, 112, - 7, 8, 9, 0, 0, 101, 0, 0, 0, 0, - 0, 25, 11, 12, 13, 14, 15, 16, 17, 18, - 19, 20, 21, 22, 23, 0, 0, 3, 4, 5, - 6, 0, 0, 0, 0, 0, 0, 0, 7, 8, - 9, 0, 0, 0, 0, 0, 0, 0, 0, 25, + 31, 41, 73, 120, 91, 47, 46, 125, 49, 50, + 31, 71, 49, 50, 175, 176, 43, 140, 7, 8, + 9, 127, 49, 50, 48, 31, 44, 150, 61, 62, + 52, 53, 54, 55, 56, 57, 40, 72, 139, 92, + 97, 98, 162, 141, 142, 93, 163, 42, 115, 31, + 31, 47, 46, 165, 71, 49, 149, 166, 121, 179, + 106, 108, 109, 110, 112, 113, 130, 58, 26, 126, + 117, 99, 100, 101, 47, 129, 171, 59, 60, 61, + 62, 128, 94, 164, 95, 167, 96, 141, 142, 172, + 144, 145, 146, 147, 153, 155, 59, 60, 61, 62, + 137, 94, 47, 143, 180, 11, 133, 131, 181, 15, + 16, 132, 134, 19, 20, 72, 135, 65, 66, 67, + 68, 69, 138, 141, 142, 150, 123, 174, 124, 156, + 178, 59, 60, 61, 62, 140, 95, 157, 136, 1, + 2, 3, 4, 5, 6, 158, 160, 159, 161, 168, + 169, 173, 7, 8, 9, 170, 182, 184, 183, 114, + 31, 118, 10, 151, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 0, 0, 0, + 0, 0, 0, 24, 0, 0, 0, 3, 4, 5, + 6, 0, 0, 0, 0, 0, 25, 111, 7, 8, + 9, 0, 26, 52, 53, 54, 55, 56, 57, 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 0, 0, 0, 71, 0, 0, 0, - 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 25, 73, 74, - 75, 76, 77, 78, 79, 80, 81, 82, 83, 115, - 0, 84, 0, 0, 0, 0, 0, 72, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 73, 74, 75, 76, 77, 78, 79, 80, 81, - 82, 83, 0, 0, 84, 4, 5, 6, 0, 0, - 0, 0, 0, 0, 0, 7, 8, 9, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 11, 12, 13, - 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 + 21, 22, 23, 0, 0, 0, 0, 0, 0, 24, + 0, 0, 3, 4, 5, 6, 0, 0, 0, 0, + 58, 0, 105, 7, 8, 9, 0, 0, 26, 0, + 59, 60, 61, 62, 0, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 0, 0, + 0, 0, 0, 0, 24, 0, 0, 0, 3, 4, + 5, 6, 0, 0, 0, 0, 0, 105, 116, 7, + 8, 9, 0, 26, 0, 0, 0, 0, 0, 0, + 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 0, 0, 0, 0, 0, 0, + 24, 3, 4, 5, 6, 0, 0, 0, 0, 0, + 0, 0, 7, 8, 9, 0, 0, 0, 0, 26, + 0, 0, 0, 0, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 0, 0, 0, + 0, 0, 0, 24, 4, 5, 6, 0, 0, 0, + 0, 0, 0, 0, 7, 8, 9, 0, 0, 0, + 0, 0, 26, 0, 0, 0, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 74, + 0, 0, 0, 0, 0, 24, 0, 75, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, + 0, 76, 77, 78, 79, 80, 81, 75, 82, 83, + 84, 85, 86, 0, 0, 87, 0, 0, 0, 0, + 0, 76, 77, 78, 79, 80, 81, 0, 82, 83, + 84, 85, 86, 0, 0, 87 }; const short parser::yycheck_[] = { - 0, 10, 36, 70, 38, 25, 86, 25, 24, 25, - 10, 24, 25, 50, 51, 24, 24, 25, 60, 60, - 59, 88, 64, 64, 24, 24, 63, 0, 9, 10, - 11, 12, 13, 14, 57, 58, 116, 46, 47, 16, - 17, 18, 40, 27, 60, 40, 46, 47, 66, 69, - 64, 69, 60, 24, 64, 122, 66, 56, 57, 58, - 59, 60, 61, 59, 61, 46, 63, 67, 52, 53, - 65, 91, 152, 91, 55, 56, 57, 58, 63, 60, - 147, 59, 149, 47, 48, 49, 153, 59, 65, 52, - 53, 125, 126, 9, 10, 11, 12, 13, 14, 119, - 167, 119, 101, 64, 171, 66, 65, 63, 107, 63, - 63, 32, 112, 55, 56, 57, 58, 20, 21, 22, - 23, 27, 156, 63, 62, 159, 55, 56, 57, 58, - 46, 60, 63, 3, 4, 5, 6, 7, 8, 55, - 56, 57, 58, 60, 64, 154, 16, 17, 18, 52, - 53, 27, 63, 63, 154, 52, 26, 66, 28, 29, - 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 173, -1, 5, 6, 7, 8, 41, 42, 43, - 44, 45, 69, 15, 16, 17, 18, -1, -1, 59, - -1, -1, -1, -1, -1, 65, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, -1, - -1, 5, 6, 7, 8, -1, -1, -1, -1, -1, - -1, -1, 16, 17, 18, -1, -1, 59, -1, -1, - -1, -1, -1, 65, 28, 29, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 39, 40, -1, -1, 5, - 6, 7, 8, -1, -1, -1, -1, -1, -1, 15, - 16, 17, 18, -1, -1, 59, -1, -1, -1, -1, - -1, 65, 28, 29, 30, 31, 32, 33, 34, 35, - 36, 37, 38, 39, 40, -1, -1, 5, 6, 7, - 8, -1, -1, -1, -1, -1, -1, -1, 16, 17, - 18, -1, -1, -1, -1, -1, -1, -1, -1, 65, + 0, 10, 38, 73, 40, 26, 26, 89, 24, 25, + 10, 40, 24, 25, 51, 52, 25, 27, 16, 17, + 18, 91, 24, 25, 0, 25, 25, 64, 58, 59, + 9, 10, 11, 12, 13, 14, 60, 66, 120, 28, + 49, 50, 61, 53, 54, 61, 65, 60, 69, 49, + 50, 72, 72, 61, 40, 24, 126, 65, 64, 61, + 59, 60, 61, 62, 63, 64, 60, 46, 66, 65, + 70, 48, 49, 50, 95, 95, 158, 56, 57, 58, + 59, 65, 61, 153, 65, 155, 67, 53, 54, 159, + 20, 21, 22, 23, 130, 131, 56, 57, 58, 59, + 64, 61, 123, 123, 174, 28, 105, 60, 178, 32, + 33, 60, 111, 36, 37, 66, 116, 41, 42, 43, + 44, 45, 64, 53, 54, 64, 62, 163, 64, 32, + 166, 56, 57, 58, 59, 27, 65, 64, 67, 3, + 4, 5, 6, 7, 8, 63, 65, 64, 61, 61, + 27, 160, 16, 17, 18, 64, 64, 53, 180, 69, + 160, 72, 26, 128, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, -1, -1, -1, + -1, -1, -1, 47, -1, -1, -1, 5, 6, 7, + 8, -1, -1, -1, -1, -1, 60, 15, 16, 17, + 18, -1, 66, 9, 10, 11, 12, 13, 14, -1, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, - 38, 39, 40, -1, -1, -1, 19, -1, -1, -1, - -1, -1, -1, -1, 27, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 65, 41, 42, - 43, 44, 45, 46, 47, 48, 49, 50, 51, 19, - -1, 54, -1, -1, -1, -1, -1, 27, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 41, 42, 43, 44, 45, 46, 47, 48, 49, - 50, 51, -1, -1, 54, 6, 7, 8, -1, -1, - -1, -1, -1, -1, -1, 16, 17, 18, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 28, 29, 30, - 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 + 38, 39, 40, -1, -1, -1, -1, -1, -1, 47, + -1, -1, 5, 6, 7, 8, -1, -1, -1, -1, + 46, -1, 60, 16, 17, 18, -1, -1, 66, -1, + 56, 57, 58, 59, -1, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, -1, -1, + -1, -1, -1, -1, 47, -1, -1, -1, 5, 6, + 7, 8, -1, -1, -1, -1, -1, 60, 15, 16, + 17, 18, -1, 66, -1, -1, -1, -1, -1, -1, + -1, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, -1, -1, -1, -1, -1, -1, + 47, 5, 6, 7, 8, -1, -1, -1, -1, -1, + -1, -1, 16, 17, 18, -1, -1, -1, -1, 66, + -1, -1, -1, -1, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, -1, -1, -1, + -1, -1, -1, 47, 6, 7, 8, -1, -1, -1, + -1, -1, -1, -1, 16, 17, 18, -1, -1, -1, + -1, -1, 66, -1, -1, -1, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 19, + -1, -1, -1, -1, -1, 47, -1, 27, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 19, + -1, 41, 42, 43, 44, 45, 46, 27, 48, 49, + 50, 51, 52, -1, -1, 55, -1, -1, -1, -1, + -1, 41, 42, 43, 44, 45, 46, -1, 48, 49, + 50, 51, 52, -1, -1, 55 }; const signed char @@ -2358,38 +2396,39 @@ namespace yy { { 0, 3, 4, 5, 6, 7, 8, 16, 17, 18, 26, 28, 29, 30, 31, 32, 33, 34, 35, 36, - 37, 38, 39, 40, 59, 65, 68, 69, 70, 71, - 72, 73, 75, 83, 85, 86, 87, 93, 59, 69, - 69, 71, 84, 85, 87, 0, 24, 25, 76, 9, - 10, 11, 12, 13, 14, 46, 55, 56, 57, 58, - 90, 91, 41, 42, 43, 44, 45, 92, 40, 65, - 93, 19, 27, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 54, 94, 95, 74, 93, 60, - 60, 64, 66, 69, 69, 47, 48, 49, 77, 79, - 81, 59, 71, 72, 71, 71, 71, 15, 71, 71, - 83, 87, 15, 72, 84, 19, 95, 63, 88, 61, - 63, 88, 64, 95, 85, 59, 59, 59, 71, 71, - 72, 66, 63, 63, 88, 27, 52, 53, 85, 20, - 21, 22, 23, 89, 95, 63, 80, 93, 78, 93, - 32, 63, 62, 63, 64, 60, 64, 95, 60, 64, - 95, 60, 27, 63, 88, 95, 69, 93, 50, 51, - 82, 93, 60, 95, 95, 63, 82, 52 + 37, 38, 39, 40, 47, 60, 66, 69, 70, 71, + 72, 73, 74, 76, 84, 86, 87, 88, 89, 95, + 60, 70, 60, 70, 72, 85, 86, 89, 0, 24, + 25, 77, 9, 10, 11, 12, 13, 14, 46, 56, + 57, 58, 59, 92, 93, 41, 42, 43, 44, 45, + 94, 40, 66, 95, 19, 27, 41, 42, 43, 44, + 45, 46, 48, 49, 50, 51, 52, 55, 96, 97, + 75, 95, 28, 61, 61, 65, 67, 70, 70, 48, + 49, 50, 78, 80, 82, 60, 72, 73, 72, 72, + 72, 15, 72, 72, 84, 89, 15, 73, 85, 19, + 97, 64, 90, 62, 64, 90, 65, 97, 65, 86, + 60, 60, 60, 72, 72, 73, 67, 64, 64, 90, + 27, 53, 54, 86, 20, 21, 22, 23, 91, 97, + 64, 87, 81, 95, 79, 95, 32, 64, 63, 64, + 65, 61, 61, 65, 97, 61, 65, 97, 61, 27, + 64, 90, 97, 70, 95, 51, 52, 83, 95, 61, + 97, 97, 64, 83, 53 }; const signed char parser::yyr1_[] = { - 0, 67, 68, 69, 69, 69, 69, 69, 69, 70, - 70, 70, 70, 70, 70, 71, 71, 71, 71, 71, - 71, 72, 72, 72, 73, 73, 73, 73, 73, 73, - 73, 74, 75, 76, 76, 76, 76, 77, 78, 78, - 79, 80, 80, 81, 82, 82, 83, 83, 84, 84, - 84, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 86, 86, - 87, 87, 87, 88, 88, 88, 89, 89, 89, 89, - 90, 90, 90, 91, 91, 91, 91, 92, 92, 92, - 92, 93, 93, 94, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95 + 0, 68, 69, 70, 70, 70, 70, 70, 70, 71, + 71, 71, 71, 71, 71, 72, 72, 72, 72, 72, + 72, 73, 73, 73, 74, 74, 74, 74, 74, 74, + 74, 75, 76, 77, 77, 77, 77, 78, 79, 79, + 80, 81, 81, 82, 83, 83, 84, 84, 85, 85, + 85, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 87, 87, 87, 87, 87, + 88, 88, 89, 89, 89, 90, 90, 90, 91, 91, + 91, 91, 92, 92, 92, 93, 93, 93, 93, 94, + 94, 94, 94, 95, 95, 96, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97 }; const signed char @@ -2401,11 +2440,11 @@ namespace yy { 1, 2, 10, 0, 2, 2, 2, 4, 2, 4, 4, 3, 5, 4, 1, 1, 3, 4, 1, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, - 1, 1, 1, 0, 2, 2, 1, 1, 1, 1, + 1, 1, 1, 2, 6, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 0, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 0, 2, 2, 1, 5, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1 + 1, 1, 1, 0, 2, 2, 1, 5, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; @@ -2424,15 +2463,15 @@ namespace yy { "\"natural0\"", "\"number\"", "\"float\"", "\"date\"", "\"UUID\"", "\"ObjectId\"", "\"link\"", "\"typed link\"", "\"argument\"", "\"beginswith\"", "\"endswith\"", "\"contains\"", "\"like\"", - "\"between\"", "\"in\"", "\"sort\"", "\"distinct\"", "\"limit\"", - "\"ascending\"", "\"descending\"", "\"@size\"", "\"@type\"", + "\"between\"", "\"in\"", "\"obj\"", "\"sort\"", "\"distinct\"", + "\"limit\"", "\"ascending\"", "\"descending\"", "\"@size\"", "\"@type\"", "\"key or value\"", "'+'", "'-'", "'*'", "'/'", "'('", "')'", "'['", "']'", "'.'", "','", "'{'", "'}'", "$accept", "final", "query", "compare", "expr", "value", "prop", "simple_prop", "subquery", "post_query", "distinct", "distinct_param", "sort", "sort_param", - "limit", "direction", "list", "list_content", "constant", "boolexpr", - "comp_type", "post_op", "aggr_op", "equality", "relational", "stringop", - "path", "path_elem", "id", YY_NULLPTR + "limit", "direction", "list", "list_content", "constant", "primary_key", + "boolexpr", "comp_type", "post_op", "aggr_op", "equality", "relational", + "stringop", "path", "path_elem", "id", YY_NULLPTR }; #endif @@ -2441,17 +2480,17 @@ namespace yy { const short parser::yyrline_[] = { - 0, 148, 148, 151, 152, 153, 154, 155, 156, 159, - 160, 165, 166, 167, 172, 175, 176, 177, 178, 179, - 180, 183, 184, 185, 188, 189, 190, 191, 192, 193, - 194, 197, 200, 203, 204, 205, 206, 208, 211, 212, - 214, 217, 218, 220, 223, 224, 226, 227, 230, 231, - 232, 235, 236, 237, 238, 239, 240, 241, 242, 243, - 244, 245, 246, 247, 248, 249, 250, 251, 255, 256, - 259, 260, 261, 264, 265, 266, 269, 270, 271, 272, - 275, 276, 277, 280, 281, 282, 283, 286, 287, 288, - 289, 292, 293, 296, 299, 300, 301, 302, 303, 304, - 305, 306, 307, 308, 309, 310, 311, 312 + 0, 149, 149, 152, 153, 154, 155, 156, 157, 160, + 161, 166, 167, 168, 173, 176, 177, 178, 179, 180, + 181, 184, 185, 186, 189, 190, 191, 192, 193, 194, + 195, 198, 201, 204, 205, 206, 207, 209, 212, 213, + 215, 218, 219, 221, 224, 225, 227, 228, 231, 232, + 233, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 257, 258, 259, 260, 261, + 264, 265, 268, 269, 270, 273, 274, 275, 278, 279, + 280, 281, 284, 285, 286, 289, 290, 291, 292, 295, + 296, 297, 298, 301, 302, 305, 308, 309, 310, 311, + 312, 313, 314, 315, 316, 317, 318, 319, 320, 321 }; void diff --git a/src/realm/parser/generated/query_bison.hpp b/src/realm/parser/generated/query_bison.hpp index b4e92210336..6889e429434 100644 --- a/src/realm/parser/generated/query_bison.hpp +++ b/src/realm/parser/generated/query_bison.hpp @@ -430,6 +430,7 @@ namespace yy { char dummy1[sizeof (AggrNode*)]; // constant + // primary_key char dummy2[sizeof (ConstantNode*)]; // distinct @@ -503,6 +504,7 @@ namespace yy { // "like" // "between" // "in" + // "obj" // "sort" // "distinct" // "limit" @@ -604,14 +606,15 @@ namespace yy { TOK_LIKE = 299, // "like" TOK_BETWEEN = 300, // "between" TOK_IN = 301, // "in" - TOK_SORT = 302, // "sort" - TOK_DISTINCT = 303, // "distinct" - TOK_LIMIT = 304, // "limit" - TOK_ASCENDING = 305, // "ascending" - TOK_DESCENDING = 306, // "descending" - TOK_SIZE = 307, // "@size" - TOK_TYPE = 308, // "@type" - TOK_KEY_VAL = 309 // "key or value" + TOK_OBJ = 302, // "obj" + TOK_SORT = 303, // "sort" + TOK_DISTINCT = 304, // "distinct" + TOK_LIMIT = 305, // "limit" + TOK_ASCENDING = 306, // "ascending" + TOK_DESCENDING = 307, // "descending" + TOK_SIZE = 308, // "@size" + TOK_TYPE = 309, // "@type" + TOK_KEY_VAL = 310 // "key or value" }; /// Backward compatibility alias (Bison 3.6). typedef token_kind_type yytokentype; @@ -628,7 +631,7 @@ namespace yy { { enum symbol_kind_type { - YYNTOKENS = 67, ///< Number of tokens. + YYNTOKENS = 68, ///< Number of tokens. SYM_YYEMPTY = -2, SYM_YYEOF = 0, // "end of file" SYM_YYerror = 1, // error @@ -677,55 +680,57 @@ namespace yy { SYM_LIKE = 44, // "like" SYM_BETWEEN = 45, // "between" SYM_IN = 46, // "in" - SYM_SORT = 47, // "sort" - SYM_DISTINCT = 48, // "distinct" - SYM_LIMIT = 49, // "limit" - SYM_ASCENDING = 50, // "ascending" - SYM_DESCENDING = 51, // "descending" - SYM_SIZE = 52, // "@size" - SYM_TYPE = 53, // "@type" - SYM_KEY_VAL = 54, // "key or value" - SYM_55_ = 55, // '+' - SYM_56_ = 56, // '-' - SYM_57_ = 57, // '*' - SYM_58_ = 58, // '/' - SYM_59_ = 59, // '(' - SYM_60_ = 60, // ')' - SYM_61_ = 61, // '[' - SYM_62_ = 62, // ']' - SYM_63_ = 63, // '.' - SYM_64_ = 64, // ',' - SYM_65_ = 65, // '{' - SYM_66_ = 66, // '}' - SYM_YYACCEPT = 67, // $accept - SYM_final = 68, // final - SYM_query = 69, // query - SYM_compare = 70, // compare - SYM_expr = 71, // expr - SYM_value = 72, // value - SYM_prop = 73, // prop - SYM_simple_prop = 74, // simple_prop - SYM_subquery = 75, // subquery - SYM_post_query = 76, // post_query - SYM_distinct = 77, // distinct - SYM_distinct_param = 78, // distinct_param - SYM_sort = 79, // sort - SYM_sort_param = 80, // sort_param - SYM_limit = 81, // limit - SYM_direction = 82, // direction - SYM_list = 83, // list - SYM_list_content = 84, // list_content - SYM_constant = 85, // constant - SYM_boolexpr = 86, // boolexpr - SYM_comp_type = 87, // comp_type - SYM_post_op = 88, // post_op - SYM_aggr_op = 89, // aggr_op - SYM_equality = 90, // equality - SYM_relational = 91, // relational - SYM_stringop = 92, // stringop - SYM_path = 93, // path - SYM_path_elem = 94, // path_elem - SYM_id = 95 // id + SYM_OBJ = 47, // "obj" + SYM_SORT = 48, // "sort" + SYM_DISTINCT = 49, // "distinct" + SYM_LIMIT = 50, // "limit" + SYM_ASCENDING = 51, // "ascending" + SYM_DESCENDING = 52, // "descending" + SYM_SIZE = 53, // "@size" + SYM_TYPE = 54, // "@type" + SYM_KEY_VAL = 55, // "key or value" + SYM_56_ = 56, // '+' + SYM_57_ = 57, // '-' + SYM_58_ = 58, // '*' + SYM_59_ = 59, // '/' + SYM_60_ = 60, // '(' + SYM_61_ = 61, // ')' + SYM_62_ = 62, // '[' + SYM_63_ = 63, // ']' + SYM_64_ = 64, // '.' + SYM_65_ = 65, // ',' + SYM_66_ = 66, // '{' + SYM_67_ = 67, // '}' + SYM_YYACCEPT = 68, // $accept + SYM_final = 69, // final + SYM_query = 70, // query + SYM_compare = 71, // compare + SYM_expr = 72, // expr + SYM_value = 73, // value + SYM_prop = 74, // prop + SYM_simple_prop = 75, // simple_prop + SYM_subquery = 76, // subquery + SYM_post_query = 77, // post_query + SYM_distinct = 78, // distinct + SYM_distinct_param = 79, // distinct_param + SYM_sort = 80, // sort + SYM_sort_param = 81, // sort_param + SYM_limit = 82, // limit + SYM_direction = 83, // direction + SYM_list = 84, // list + SYM_list_content = 85, // list_content + SYM_constant = 86, // constant + SYM_primary_key = 87, // primary_key + SYM_boolexpr = 88, // boolexpr + SYM_comp_type = 89, // comp_type + SYM_post_op = 90, // post_op + SYM_aggr_op = 91, // aggr_op + SYM_equality = 92, // equality + SYM_relational = 93, // relational + SYM_stringop = 94, // stringop + SYM_path = 95, // path + SYM_path_elem = 96, // path_elem + SYM_id = 97 // id }; }; @@ -765,6 +770,7 @@ namespace yy { break; case symbol_kind::SYM_constant: // constant + case symbol_kind::SYM_primary_key: // primary_key value.move< ConstantNode* > (std::move (that.value)); break; @@ -853,6 +859,7 @@ namespace yy { case symbol_kind::SYM_LIKE: // "like" case symbol_kind::SYM_BETWEEN: // "between" case symbol_kind::SYM_IN: // "in" + case symbol_kind::SYM_OBJ: // "obj" case symbol_kind::SYM_SORT: // "sort" case symbol_kind::SYM_DISTINCT: // "distinct" case symbol_kind::SYM_LIMIT: // "limit" @@ -1136,6 +1143,7 @@ switch (yykind) break; case symbol_kind::SYM_constant: // constant + case symbol_kind::SYM_primary_key: // primary_key value.template destroy< ConstantNode* > (); break; @@ -1224,6 +1232,7 @@ switch (yykind) case symbol_kind::SYM_LIKE: // "like" case symbol_kind::SYM_BETWEEN: // "between" case symbol_kind::SYM_IN: // "in" + case symbol_kind::SYM_OBJ: // "obj" case symbol_kind::SYM_SORT: // "sort" case symbol_kind::SYM_DISTINCT: // "distinct" case symbol_kind::SYM_LIMIT: // "limit" @@ -2110,6 +2119,21 @@ switch (yykind) return symbol_type (token::TOK_IN, v); } #endif +#if 201103L <= YY_CPLUSPLUS + static + symbol_type + make_OBJ (std::string v) + { + return symbol_type (token::TOK_OBJ, std::move (v)); + } +#else + static + symbol_type + make_OBJ (const std::string& v) + { + return symbol_type (token::TOK_OBJ, v); + } +#endif #if 201103L <= YY_CPLUSPLUS static symbol_type @@ -2558,9 +2582,9 @@ switch (yykind) /// Constants. enum { - yylast_ = 429, ///< Last index in yytable_. - yynnts_ = 29, ///< Number of nonterminal symbols. - yyfinal_ = 45 ///< Termination state number. + yylast_ = 455, ///< Last index in yytable_. + yynnts_ = 30, ///< Number of nonterminal symbols. + yyfinal_ = 48 ///< Termination state number. }; @@ -2584,15 +2608,15 @@ switch (yykind) 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 59, 60, 57, 55, 64, 56, 63, 58, 2, 2, + 60, 61, 58, 56, 65, 57, 64, 59, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 61, 2, 62, 2, 2, 2, 2, 2, 2, + 2, 62, 2, 63, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 65, 2, 66, 2, 2, 2, 2, + 2, 2, 2, 66, 2, 67, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -2610,10 +2634,11 @@ switch (yykind) 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, - 45, 46, 47, 48, 49, 50, 51, 52, 53, 54 + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55 }; // Last valid token kind. - const int code_max = 309; + const int code_max = 310; if (t <= 0) return symbol_kind::SYM_YYEOF; @@ -2636,6 +2661,7 @@ switch (yykind) break; case symbol_kind::SYM_constant: // constant + case symbol_kind::SYM_primary_key: // primary_key value.copy< ConstantNode* > (YY_MOVE (that.value)); break; @@ -2724,6 +2750,7 @@ switch (yykind) case symbol_kind::SYM_LIKE: // "like" case symbol_kind::SYM_BETWEEN: // "between" case symbol_kind::SYM_IN: // "in" + case symbol_kind::SYM_OBJ: // "obj" case symbol_kind::SYM_SORT: // "sort" case symbol_kind::SYM_DISTINCT: // "distinct" case symbol_kind::SYM_LIMIT: // "limit" @@ -2773,6 +2800,7 @@ switch (yykind) break; case symbol_kind::SYM_constant: // constant + case symbol_kind::SYM_primary_key: // primary_key value.move< ConstantNode* > (YY_MOVE (s.value)); break; @@ -2861,6 +2889,7 @@ switch (yykind) case symbol_kind::SYM_LIKE: // "like" case symbol_kind::SYM_BETWEEN: // "between" case symbol_kind::SYM_IN: // "in" + case symbol_kind::SYM_OBJ: // "obj" case symbol_kind::SYM_SORT: // "sort" case symbol_kind::SYM_DISTINCT: // "distinct" case symbol_kind::SYM_LIMIT: // "limit" diff --git a/src/realm/parser/generated/query_flex.cpp b/src/realm/parser/generated/query_flex.cpp index 7cdb47554ae..62f55567f24 100644 --- a/src/realm/parser/generated/query_flex.cpp +++ b/src/realm/parser/generated/query_flex.cpp @@ -501,8 +501,8 @@ static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner ); /* %% [3.0] code to copy yytext_ptr to yytext[] goes here, if %array \ */\ yyg->yy_c_buf_p = yy_cp; /* %% [4.0] data tables for the DFA and the user's section 1 definitions go here */ -#define YY_NUM_RULES 62 -#define YY_END_OF_BUFFER 63 +#define YY_NUM_RULES 63 +#define YY_END_OF_BUFFER 64 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info @@ -510,50 +510,50 @@ struct yy_trans_info flex_int32_t yy_verify; flex_int32_t yy_nxt; }; -static const flex_int16_t yy_accept[388] = +static const flex_int16_t yy_accept[390] = { 0, - 0, 0, 63, 61, 1, 2, 14, 61, 60, 61, - 61, 9, 3, 3, 9, 51, 51, 7, 4, 8, - 61, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 9, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 61, 61, 61, 61, 1, - 2, 6, 0, 58, 0, 60, 52, 0, 0, 0, - 0, 12, 0, 59, 0, 0, 53, 0, 0, 56, - 0, 56, 51, 0, 0, 55, 10, 4, 11, 0, - 0, 0, 0, 0, 0, 0, 0, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 5, - - 60, 60, 60, 60, 60, 60, 60, 49, 13, 60, - 60, 60, 0, 60, 60, 60, 60, 0, 60, 60, - 60, 60, 60, 60, 60, 60, 13, 60, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 56, 0, 56, - 0, 55, 54, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 16, 12, 15, 28, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 43, 0, 60, 60, - 44, 45, 60, 14, 60, 60, 60, 60, 0, 0, - 60, 60, 40, 60, 60, 60, 60, 60, 60, 0, - 0, 0, 0, 43, 44, 0, 56, 0, 35, 0, - - 0, 0, 32, 33, 0, 34, 0, 0, 60, 0, - 60, 60, 60, 29, 60, 60, 60, 60, 60, 50, - 21, 60, 17, 45, 25, 60, 0, 48, 41, 60, - 60, 0, 60, 0, 0, 0, 0, 0, 38, 0, - 31, 37, 0, 60, 57, 0, 60, 60, 60, 60, - 60, 60, 42, 60, 60, 27, 60, 60, 0, 0, - 0, 0, 0, 0, 36, 0, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 0, 0, 0, - 0, 39, 60, 60, 22, 60, 60, 60, 60, 60, - 60, 60, 60, 0, 0, 0, 0, 60, 60, 20, - - 60, 26, 19, 60, 43, 30, 60, 0, 0, 43, - 0, 28, 60, 60, 60, 60, 0, 0, 0, 18, - 29, 60, 60, 0, 0, 48, 60, 60, 0, 0, - 0, 60, 60, 0, 0, 48, 60, 23, 0, 0, - 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 64, 62, 1, 2, 14, 62, 61, 62, + 62, 9, 3, 3, 9, 52, 52, 7, 4, 8, + 62, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 9, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 62, 62, 62, 62, 1, + 2, 6, 0, 59, 0, 61, 53, 0, 0, 0, + 0, 12, 0, 60, 0, 0, 54, 0, 0, 57, + 0, 57, 52, 0, 0, 56, 10, 4, 11, 0, + 0, 0, 0, 0, 0, 0, 0, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 5, + + 61, 61, 61, 61, 61, 61, 61, 50, 61, 13, + 61, 61, 61, 0, 61, 61, 61, 61, 0, 61, + 61, 61, 61, 61, 61, 61, 61, 13, 61, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, + 57, 0, 56, 55, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 16, 12, 15, 29, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 44, 0, 61, + 61, 45, 46, 61, 14, 61, 28, 61, 61, 61, + 0, 0, 61, 61, 41, 61, 61, 61, 61, 61, + 61, 0, 0, 0, 0, 44, 45, 0, 57, 0, + + 36, 0, 0, 0, 33, 34, 0, 35, 0, 0, + 61, 0, 61, 61, 61, 30, 61, 61, 61, 61, + 61, 51, 21, 61, 17, 46, 25, 61, 0, 49, + 42, 61, 61, 0, 61, 0, 0, 0, 0, 0, + 39, 0, 32, 38, 0, 61, 58, 0, 61, 61, + 61, 61, 61, 61, 43, 61, 61, 27, 61, 61, + 0, 0, 0, 0, 0, 0, 37, 0, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 0, + 0, 0, 0, 40, 61, 61, 22, 61, 61, 61, + 61, 61, 61, 61, 61, 0, 0, 0, 0, 61, + + 61, 20, 61, 26, 19, 61, 44, 31, 61, 0, + 0, 44, 0, 29, 61, 61, 61, 61, 0, 0, + 0, 18, 30, 61, 61, 0, 0, 49, 61, 61, + 0, 0, 0, 61, 61, 0, 0, 49, 61, 23, + 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 46, 0 + 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 47, 0 } ; static const YY_CHAR yy_ec[256] = @@ -566,147 +566,147 @@ static const YY_CHAR yy_ec[256] = 19, 20, 19, 21, 19, 19, 19, 22, 1, 23, 24, 25, 1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, - 43, 44, 45, 46, 47, 36, 48, 49, 50, 36, - 51, 52, 53, 1, 54, 1, 55, 56, 57, 58, - - 59, 60, 61, 62, 63, 36, 64, 65, 66, 67, - 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, - 78, 79, 14, 80, 14, 1, 1, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 1, 1, 82, 82, 82, 82, 82, 82, 82, - - 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, - 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, - 82, 82, 82, 83, 83, 83, 83, 83, 83, 83, - 83, 83, 83, 83, 83, 83, 83, 83, 83, 84, - 84, 84, 84, 84, 84, 84, 84, 1, 1, 1, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 48, + 52, 53, 54, 1, 55, 1, 56, 57, 58, 59, + + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 14, 82, 14, 1, 1, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 1, 1, 84, 84, 84, 84, 84, 84, 84, + + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 86, + 86, 86, 86, 86, 86, 86, 86, 1, 1, 1, 1, 1, 1, 1, 1 } ; -static const YY_CHAR yy_meta[85] = +static const YY_CHAR yy_meta[87] = { 0, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 2, 1, 3, 1, 2, 4, 4, 4, 4, 1, 1, 2, 1, 1, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 1, 3, 1, 3, 4, 4, 4, 4, 4, 4, + 5, 1, 3, 1, 3, 4, 4, 4, 4, 4, + 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 1, - 1, 3, 3, 3 + 5, 1, 1, 3, 3, 3 } ; -static const flex_int16_t yy_base[457] = +static const flex_int16_t yy_base[459] = { 0, - 0, 0, 668, 1764, 83, 663, 641, 80, 70, 655, - 83, 1764, 1764, 77, 81, 88, 108, 86, 90, 638, - 105, 121, 99, 133, 155, 139, 145, 73, 158, 212, - 208, 232, 215, 287, 604, 273, 227, 277, 281, 284, - 327, 331, 344, 337, 260, 579, 575, 574, 570, 114, - 627, 1764, 132, 1764, 416, 112, 379, 207, 548, 544, - 543, 1764, 124, 1764, 426, 331, 420, 54, 165, 386, - 429, 435, 486, 495, 0, 1764, 1764, 1764, 1764, 548, - 551, 558, 551, 79, 139, 535, 553, 436, 473, 469, - 481, 476, 489, 502, 495, 516, 544, 528, 538, 549, - - 555, 622, 552, 605, 608, 612, 615, 629, 294, 632, - 626, 649, 439, 703, 707, 683, 687, 553, 691, 710, - 698, 731, 694, 751, 754, 747, 1764, 665, 524, 516, - 0, 514, 513, 0, 176, 96, 805, 1764, 824, 828, - 832, 836, 0, 531, 509, 504, 508, 493, 482, 469, - 478, 474, 473, 780, 788, 808, 827, 815, 830, 852, - 843, 849, 856, 870, 878, 892, 906, 855, 914, 920, - 865, 911, 925, 928, 940, 934, 954, 976, 1011, 1029, - 982, 1004, 1764, 1000, 1009, 991, 1017, 1026, 1037, 456, - 0, 455, 0, 191, 1764, 900, 1084, 1093, 1764, 467, - - 460, 466, 1764, 1764, 470, 1764, 468, 450, 1066, 506, - 1075, 1086, 1070, 1103, 1108, 1099, 1115, 1125, 1121, 1110, - 1111, 1150, 1128, 1137, 1154, 1166, 1196, 1206, 1187, 1193, - 1199, 0, 1190, 0, 0, 179, 1234, 436, 1764, 420, - 1764, 1764, 432, 1227, 1764, 479, 1214, 1232, 1243, 1250, - 1253, 1266, 1261, 1270, 1288, 1276, 1310, 1294, 0, 0, - 0, 0, 209, 1361, 1764, 412, 1321, 1315, 1334, 1343, - 1338, 1356, 1360, 1363, 1377, 1380, 1397, 0, 0, 188, - 1447, 1764, 1418, 1436, 1305, 1431, 1442, 1458, 1455, 1464, - 1477, 1480, 1491, 0, 0, 195, 1434, 1517, 1499, 1470, - - 1504, 1483, 1524, 1527, 1532, 1537, 1542, 0, 0, 1764, - 1609, 1550, 1561, 1585, 1600, 1607, 0, 0, 1454, 1565, - 1588, 1621, 1624, 0, 0, 1635, 1633, 1628, 0, 0, - 1472, 1640, 1650, 0, 0, 1572, 1662, 1613, 0, 466, - 1646, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 462, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 460, 0, 0, 0, 0, 0, 0, 0, 0, 462, - 456, 1764, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 459, 1764, 1764, 1746, 1749, 1754, - 465, 463, 461, 460, 448, 1758, 438, 433, 426, 425, - - 420, 419, 412, 399, 389, 388, 384, 383, 382, 380, - 376, 369, 368, 349, 337, 333, 331, 326, 324, 323, - 322, 320, 319, 318, 316, 313, 297, 289, 284, 283, - 281, 277, 273, 266, 265, 264, 262, 261, 255, 253, - 244, 220, 208, 205, 195, 185, 183, 171, 146, 145, - 142, 141, 139, 137, 127, 101 + 0, 0, 691, 1762, 85, 686, 653, 82, 72, 662, + 85, 1762, 1762, 79, 83, 90, 111, 88, 92, 643, + 78, 109, 139, 120, 149, 134, 112, 126, 161, 208, + 213, 231, 226, 288, 605, 260, 283, 268, 278, 295, + 330, 338, 343, 348, 179, 579, 577, 575, 567, 116, + 646, 1762, 122, 1762, 429, 303, 389, 212, 564, 559, + 556, 1762, 113, 1762, 434, 218, 439, 119, 159, 458, + 319, 493, 507, 448, 0, 1762, 1762, 1762, 1762, 561, + 566, 557, 552, 77, 100, 512, 534, 455, 491, 477, + 499, 463, 518, 448, 512, 521, 529, 534, 541, 538, + + 568, 612, 598, 588, 602, 622, 618, 625, 640, 364, + 674, 664, 646, 591, 715, 719, 667, 708, 535, 689, + 727, 724, 712, 730, 773, 776, 595, 1762, 735, 498, + 495, 0, 490, 486, 0, 209, 202, 812, 1762, 819, + 824, 829, 845, 0, 498, 480, 475, 477, 466, 474, + 456, 468, 460, 453, 783, 803, 817, 823, 829, 840, + 843, 859, 865, 884, 887, 893, 902, 921, 853, 905, + 928, 909, 913, 949, 931, 958, 955, 969, 973, 977, + 859, 1051, 992, 1007, 1762, 1020, 1015, 1026, 1029, 1043, + 1049, 432, 0, 414, 0, 218, 1762, 879, 1065, 1098, + + 1762, 427, 420, 426, 1762, 1762, 425, 1762, 424, 405, + 1067, 474, 1085, 1090, 1103, 1108, 1096, 1113, 1124, 1142, + 1132, 1126, 1056, 1137, 1079, 1147, 1150, 1166, 1186, 1219, + 1172, 1188, 1193, 0, 1176, 0, 0, 217, 1245, 397, + 1762, 397, 1762, 1762, 405, 1217, 1762, 458, 1222, 1237, + 1234, 1240, 1246, 1275, 1258, 1263, 1282, 1228, 1302, 1292, + 0, 0, 0, 0, 227, 1264, 1762, 388, 1305, 1321, + 1310, 1331, 1345, 1327, 1357, 1349, 1372, 1375, 1392, 0, + 0, 244, 1418, 1762, 1410, 1416, 1400, 1419, 1435, 1422, + 1428, 1456, 1464, 1471, 1480, 0, 0, 269, 1333, 1485, + + 1477, 1474, 1514, 1490, 1493, 1538, 1500, 1519, 1553, 0, + 0, 1762, 1574, 1527, 1556, 1548, 1567, 1592, 0, 0, + 1516, 1562, 1572, 1601, 1608, 0, 0, 1647, 1617, 1629, + 0, 0, 1608, 1635, 1652, 0, 0, 1677, 1658, 1638, + 0, 441, 1646, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 439, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 438, 0, 0, 0, 0, 0, 0, 0, + 0, 434, 429, 1762, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 430, 1762, 1762, 1744, + 1747, 1752, 435, 433, 432, 427, 426, 1756, 422, 421, + + 416, 414, 408, 401, 400, 399, 395, 394, 393, 389, + 381, 374, 372, 371, 367, 366, 356, 354, 351, 347, + 346, 338, 331, 329, 324, 322, 321, 319, 318, 279, + 276, 274, 272, 270, 264, 254, 252, 240, 227, 218, + 217, 213, 204, 203, 196, 187, 185, 181, 178, 177, + 173, 163, 159, 133, 120, 119, 103, 92 } ; -static const flex_int16_t yy_def[457] = +static const flex_int16_t yy_def[459] = { 0, - 387, 1, 387, 387, 387, 387, 387, 388, 389, 387, - 390, 387, 387, 387, 387, 387, 387, 387, 387, 387, - 387, 389, 389, 389, 389, 389, 389, 389, 389, 389, - 389, 389, 389, 389, 387, 389, 389, 389, 389, 389, - 389, 389, 389, 389, 389, 387, 387, 387, 387, 387, - 387, 387, 388, 387, 387, 389, 389, 387, 387, 387, - 387, 387, 390, 387, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 387, 391, 387, 387, 387, 387, 387, - 387, 387, 387, 387, 387, 387, 387, 389, 389, 389, + 389, 1, 389, 389, 389, 389, 389, 390, 391, 389, + 392, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 389, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 389, 389, 389, 389, 389, + 389, 389, 390, 389, 389, 391, 391, 389, 389, 389, + 389, 389, 392, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 393, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 391, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 391, 391, 391, 389, 391, 391, 391, 391, 389, 391, + 391, 391, 391, 391, 391, 391, 391, 389, 391, 389, + 389, 394, 389, 389, 395, 389, 389, 389, 389, 389, + 389, 389, 389, 393, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 391, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 389, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 389, 389, 391, 391, 389, 391, 391, 391, 391, 391, + 391, 389, 396, 389, 397, 389, 389, 389, 389, 389, + + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 391, 398, 391, 391, 391, 391, 391, 391, 391, 391, + 391, 389, 391, 391, 391, 391, 391, 391, 389, 389, + 391, 391, 391, 399, 391, 400, 401, 389, 389, 389, + 389, 389, 389, 389, 389, 391, 389, 398, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 402, 403, 404, 405, 389, 389, 389, 389, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 406, + 407, 389, 389, 389, 391, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 408, 409, 389, 389, 391, + + 391, 391, 391, 391, 391, 391, 391, 391, 391, 410, + 411, 389, 389, 391, 391, 391, 391, 391, 412, 413, + 389, 391, 391, 391, 391, 414, 415, 389, 391, 391, + 416, 417, 389, 391, 391, 418, 419, 389, 391, 391, + 420, 389, 391, 421, 422, 423, 424, 425, 426, 427, + 428, 429, 389, 430, 431, 432, 433, 434, 435, 436, + 437, 438, 389, 439, 440, 441, 442, 443, 444, 445, + 446, 389, 389, 389, 447, 448, 449, 450, 451, 452, + 453, 454, 455, 456, 457, 458, 389, 389, 0, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, - 389, 389, 387, 389, 389, 389, 389, 387, 389, 389, - 389, 389, 389, 389, 389, 389, 387, 389, 387, 387, - 392, 387, 387, 393, 387, 387, 387, 387, 387, 387, - 387, 387, 391, 387, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 389, 389, 389, 389, 389, 389, 389, - 389, 389, 389, 389, 389, 389, 389, 387, 389, 389, - 389, 389, 389, 389, 389, 389, 389, 389, 387, 387, - 389, 389, 387, 389, 389, 389, 389, 389, 389, 387, - 394, 387, 395, 387, 387, 387, 387, 387, 387, 387, - - 387, 387, 387, 387, 387, 387, 387, 387, 389, 396, - 389, 389, 389, 389, 389, 389, 389, 389, 389, 387, - 389, 389, 389, 389, 389, 389, 387, 387, 389, 389, - 389, 397, 389, 398, 399, 387, 387, 387, 387, 387, - 387, 387, 387, 389, 387, 396, 389, 389, 389, 389, - 389, 389, 389, 389, 389, 389, 389, 389, 400, 401, - 402, 403, 387, 387, 387, 387, 389, 389, 389, 389, - 389, 389, 389, 389, 389, 389, 389, 404, 405, 387, - 387, 387, 389, 389, 389, 389, 389, 389, 389, 389, - 389, 389, 389, 406, 407, 387, 387, 389, 389, 389, - - 389, 389, 389, 389, 389, 389, 389, 408, 409, 387, - 387, 389, 389, 389, 389, 389, 410, 411, 387, 389, - 389, 389, 389, 412, 413, 387, 389, 389, 414, 415, - 387, 389, 389, 416, 417, 387, 389, 389, 418, 387, - 389, 419, 420, 421, 422, 423, 424, 425, 426, 427, - 387, 428, 429, 430, 431, 432, 433, 434, 435, 436, - 387, 437, 438, 439, 440, 441, 442, 443, 444, 387, - 387, 387, 445, 446, 447, 448, 449, 450, 451, 452, - 453, 454, 455, 456, 387, 387, 0, 387, 387, 387, - 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, - - 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 387, 387, 387 + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 389 } ; static const flex_int16_t yy_nxt[1849] = @@ -716,204 +716,204 @@ static const flex_int16_t yy_nxt[1849] = 17, 4, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 28, 29, 28, 28, 30, 28, 31, 32, 28, 28, 28, 33, 34, 28, 28, 28, 28, - 35, 4, 12, 28, 36, 37, 24, 25, 26, 38, - 28, 28, 39, 28, 40, 28, 41, 42, 28, 28, - 28, 43, 44, 45, 28, 28, 28, 28, 28, 46, - 4, 47, 48, 49, 50, 54, 50, 57, 57, 57, - 57, 64, 66, 135, 67, 67, 67, 67, 70, 70, - - 70, 70, 71, 72, 385, 73, 73, 73, 73, 77, - 52, 68, 77, 78, 79, 50, 69, 50, 74, 92, - 135, 58, 71, 72, 58, 73, 73, 73, 73, 93, - 384, 55, 64, 148, 65, 195, 75, 54, 74, 68, - 383, 149, 382, 69, 381, 380, 74, 76, 379, 378, - 58, 59, 60, 61, 59, 60, 61, 93, 88, 80, - 89, 81, 195, 58, 75, 90, 74, 76, 82, 83, - 84, 98, 58, 94, 377, 65, 85, 86, 97, 87, - 59, 60, 61, 55, 58, 95, 376, 91, 375, 96, - 58, 136, 90, 59, 60, 61, 58, 100, 374, 99, - - 94, 150, 59, 60, 61, 97, 58, 194, 371, 58, - 56, 370, 151, 95, 59, 60, 61, 96, 263, 136, - 59, 60, 61, 369, 101, 236, 59, 60, 61, 102, - 102, 102, 102, 296, 104, 194, 59, 60, 61, 59, - 60, 61, 105, 280, 310, 263, 103, 368, 106, 108, - 108, 108, 108, 236, 107, 110, 367, 93, 366, 58, - 296, 111, 104, 58, 365, 364, 58, 362, 361, 360, - 105, 280, 310, 56, 103, 109, 359, 56, 58, 56, - 358, 107, 112, 58, 357, 93, 356, 355, 111, 59, - 60, 61, 354, 59, 60, 61, 59, 60, 61, 113, - - 352, 114, 109, 99, 115, 115, 115, 115, 59, 60, - 61, 58, 91, 59, 60, 61, 351, 90, 103, 350, - 101, 349, 348, 347, 58, 346, 345, 344, 58, 342, - 116, 121, 58, 126, 340, 58, 339, 119, 58, 120, - 335, 59, 60, 61, 90, 58, 103, 100, 70, 70, - 70, 70, 334, 104, 59, 60, 61, 117, 59, 60, - 61, 105, 59, 60, 61, 59, 60, 61, 59, 60, - 61, 330, 329, 107, 109, 59, 60, 61, 58, 325, - 117, 104, 58, 324, 112, 318, 317, 309, 58, 105, - 111, 308, 295, 123, 122, 58, 57, 57, 57, 57, - - 107, 109, 294, 70, 70, 70, 70, 125, 59, 60, - 61, 124, 59, 60, 61, 63, 137, 111, 59, 60, - 61, 53, 53, 279, 53, 59, 60, 61, 278, 262, - 58, 63, 53, 53, 63, 72, 261, 67, 67, 67, - 67, 259, 63, 63, 137, 138, 139, 139, 139, 139, - 74, 235, 140, 140, 140, 140, 179, 179, 179, 179, - 59, 60, 61, 234, 193, 137, 191, 53, 143, 386, - 373, 53, 372, 154, 363, 53, 353, 63, 74, 76, - 343, 63, 53, 282, 245, 63, 53, 58, 53, 131, - 266, 265, 63, 137, 138, 158, 63, 157, 63, 134, - - 71, 72, 155, 73, 73, 73, 73, 141, 241, 141, - 155, 245, 142, 142, 142, 142, 74, 59, 60, 61, - 58, 159, 156, 243, 58, 157, 242, 58, 241, 240, - 155, 239, 58, 238, 160, 56, 128, 208, 155, 162, - 58, 161, 207, 206, 74, 76, 58, 205, 204, 159, - 59, 60, 61, 58, 59, 60, 61, 59, 60, 61, - 163, 160, 59, 60, 61, 165, 162, 58, 161, 203, - 59, 60, 61, 164, 202, 166, 59, 60, 61, 58, - 167, 201, 200, 59, 60, 61, 167, 163, 169, 58, - 170, 199, 166, 192, 56, 58, 190, 59, 60, 61, - - 58, 164, 166, 58, 128, 183, 58, 153, 167, 59, - 60, 61, 152, 147, 167, 169, 146, 170, 145, 59, - 60, 61, 144, 133, 132, 59, 60, 61, 56, 51, - 59, 60, 61, 59, 60, 61, 59, 60, 61, 102, - 102, 102, 102, 168, 171, 172, 108, 108, 108, 108, - 130, 173, 175, 178, 129, 128, 58, 174, 127, 58, - 118, 79, 62, 58, 52, 51, 58, 387, 387, 387, - 176, 171, 172, 58, 387, 177, 387, 58, 387, 175, - 58, 178, 387, 58, 387, 387, 59, 60, 61, 59, - 60, 61, 177, 59, 60, 61, 59, 60, 61, 387, - - 58, 387, 177, 59, 60, 61, 387, 59, 60, 61, - 59, 60, 61, 59, 60, 61, 58, 387, 387, 177, - 115, 115, 115, 115, 115, 115, 115, 115, 180, 181, - 59, 60, 61, 182, 58, 166, 387, 387, 58, 155, - 387, 387, 58, 387, 387, 58, 59, 60, 61, 58, - 387, 186, 387, 387, 58, 154, 182, 387, 58, 387, - 182, 58, 184, 387, 59, 60, 61, 155, 59, 60, - 61, 387, 59, 60, 61, 59, 60, 61, 387, 59, - 60, 61, 58, 387, 59, 60, 61, 156, 59, 60, - 61, 59, 60, 61, 177, 387, 387, 185, 58, 387, - - 182, 387, 58, 174, 387, 58, 387, 387, 387, 189, - 387, 387, 59, 60, 61, 387, 187, 196, 387, 196, - 210, 177, 197, 197, 197, 197, 387, 188, 59, 60, - 61, 58, 59, 60, 61, 59, 60, 61, 198, 58, - 387, 139, 139, 139, 139, 140, 140, 140, 140, 142, - 142, 142, 142, 142, 142, 142, 142, 209, 137, 58, - 387, 59, 60, 61, 211, 387, 58, 387, 387, 59, - 60, 61, 220, 220, 220, 220, 387, 214, 58, 387, - 387, 58, 387, 387, 387, 209, 137, 138, 213, 59, - 60, 61, 211, 387, 58, 76, 59, 60, 61, 212, - - 58, 215, 387, 58, 387, 214, 387, 58, 59, 60, - 61, 59, 60, 61, 216, 213, 58, 197, 197, 197, - 197, 58, 217, 387, 59, 60, 61, 212, 215, 58, - 59, 60, 61, 59, 60, 61, 218, 59, 60, 61, - 219, 216, 387, 58, 221, 387, 59, 60, 61, 218, - 387, 59, 60, 61, 222, 223, 387, 58, 387, 59, - 60, 61, 58, 218, 156, 58, 387, 387, 219, 387, - 387, 58, 221, 59, 60, 61, 58, 224, 387, 58, - 387, 387, 222, 387, 387, 58, 387, 59, 60, 61, - 387, 58, 59, 60, 61, 59, 60, 61, 387, 225, - - 232, 59, 60, 61, 224, 58, 59, 60, 61, 59, - 60, 61, 229, 387, 387, 59, 60, 61, 226, 387, - 387, 59, 60, 61, 387, 387, 225, 58, 179, 179, - 179, 179, 180, 58, 230, 59, 60, 61, 387, 387, - 230, 227, 58, 227, 218, 226, 228, 228, 228, 228, - 387, 58, 387, 387, 387, 58, 230, 59, 60, 61, - 58, 387, 230, 59, 60, 61, 387, 223, 58, 387, - 387, 231, 59, 60, 61, 156, 387, 58, 387, 387, - 387, 59, 60, 61, 229, 59, 60, 61, 58, 387, - 59, 60, 61, 387, 233, 387, 249, 387, 59, 60, - - 61, 197, 197, 197, 197, 244, 387, 59, 60, 61, - 237, 237, 237, 237, 247, 387, 248, 58, 59, 60, - 61, 58, 387, 387, 249, 387, 58, 220, 220, 220, - 220, 387, 244, 250, 387, 387, 387, 58, 387, 387, - 387, 247, 251, 138, 248, 253, 252, 59, 60, 61, - 58, 59, 60, 61, 58, 254, 59, 60, 61, 58, - 255, 250, 58, 387, 387, 387, 58, 59, 60, 61, - 251, 387, 58, 254, 252, 387, 58, 387, 387, 58, - 59, 60, 61, 254, 59, 60, 61, 255, 58, 59, - 60, 61, 59, 60, 61, 256, 59, 60, 61, 260, - - 387, 58, 59, 60, 61, 58, 59, 60, 61, 59, - 60, 61, 257, 228, 228, 228, 228, 58, 59, 60, - 61, 387, 256, 228, 228, 228, 228, 387, 258, 254, - 387, 59, 60, 61, 258, 59, 60, 61, 58, 257, - 387, 58, 387, 387, 58, 387, 387, 59, 60, 61, - 58, 237, 237, 237, 237, 258, 267, 253, 268, 264, - 387, 258, 269, 387, 387, 58, 387, 387, 59, 60, - 61, 59, 60, 61, 59, 60, 61, 270, 58, 264, - 59, 60, 61, 58, 267, 268, 387, 387, 387, 271, - 269, 387, 272, 387, 58, 59, 60, 61, 387, 387, - - 273, 58, 274, 387, 58, 270, 387, 387, 59, 60, - 61, 274, 58, 59, 60, 61, 271, 58, 387, 272, - 387, 58, 275, 387, 59, 60, 61, 58, 273, 274, - 387, 59, 60, 61, 59, 60, 61, 277, 274, 58, - 276, 387, 59, 60, 61, 58, 387, 59, 60, 61, - 275, 59, 60, 61, 387, 283, 58, 59, 60, 61, - 387, 58, 284, 387, 277, 387, 58, 287, 276, 59, - 60, 61, 58, 285, 387, 59, 60, 61, 281, 281, - 281, 281, 286, 283, 288, 58, 59, 60, 61, 58, - 284, 59, 60, 61, 58, 287, 59, 60, 61, 387, - - 285, 387, 59, 60, 61, 289, 290, 58, 387, 286, - 387, 58, 288, 387, 58, 59, 60, 61, 387, 59, - 60, 61, 291, 292, 59, 60, 61, 293, 58, 387, - 387, 58, 289, 290, 387, 387, 387, 59, 60, 61, - 387, 59, 60, 61, 59, 60, 61, 387, 58, 291, - 292, 311, 311, 311, 311, 293, 387, 298, 59, 60, - 61, 59, 60, 61, 281, 281, 281, 281, 297, 58, - 299, 326, 326, 326, 326, 300, 301, 387, 59, 60, - 61, 387, 58, 387, 298, 387, 387, 58, 303, 336, - 336, 336, 336, 58, 304, 387, 387, 387, 299, 59, - - 60, 61, 300, 302, 301, 387, 58, 387, 387, 58, - 387, 387, 59, 60, 61, 58, 303, 59, 60, 61, - 307, 58, 304, 59, 60, 61, 305, 387, 58, 306, - 302, 58, 387, 387, 58, 387, 59, 60, 61, 59, - 60, 61, 58, 314, 313, 59, 60, 61, 307, 312, - 58, 59, 60, 61, 305, 58, 315, 306, 59, 60, - 61, 59, 60, 61, 59, 60, 61, 387, 58, 387, - 314, 313, 59, 60, 61, 58, 316, 312, 58, 387, - 59, 60, 61, 58, 315, 59, 60, 61, 58, 336, - 336, 336, 336, 58, 320, 387, 387, 387, 59, 60, - - 61, 58, 387, 387, 316, 59, 60, 61, 59, 60, - 61, 387, 58, 59, 60, 61, 58, 321, 59, 60, - 61, 387, 320, 59, 60, 61, 311, 311, 311, 311, - 319, 59, 60, 61, 322, 323, 58, 387, 387, 58, - 387, 387, 59, 60, 61, 321, 59, 60, 61, 327, - 328, 58, 326, 326, 326, 326, 331, 387, 58, 332, - 387, 387, 322, 323, 58, 387, 59, 60, 61, 59, - 60, 61, 58, 333, 387, 58, 387, 327, 328, 58, - 338, 59, 60, 61, 58, 337, 387, 332, 59, 60, - 61, 58, 341, 387, 59, 60, 61, 58, 387, 387, - - 333, 58, 59, 60, 61, 59, 60, 61, 338, 59, - 60, 61, 337, 58, 59, 60, 61, 387, 387, 387, - 341, 59, 60, 61, 387, 387, 387, 59, 60, 61, - 387, 59, 60, 61, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 59, 60, 61, 53, 53, 53, 53, - 53, 56, 56, 56, 63, 63, 63, 63, 63, 246, - 387, 246, 246, 3, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, - - 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 387, 387, 387, 387, 387 + 28, 35, 4, 12, 28, 36, 37, 24, 25, 26, + 38, 28, 28, 39, 28, 28, 40, 28, 41, 42, + 28, 28, 28, 43, 44, 45, 28, 28, 28, 28, + 28, 46, 4, 47, 48, 49, 50, 54, 50, 57, + 57, 57, 57, 64, 66, 387, 67, 67, 67, 67, + + 70, 70, 70, 70, 71, 72, 386, 73, 73, 73, + 73, 77, 52, 68, 77, 78, 79, 50, 69, 50, + 74, 64, 385, 384, 58, 71, 72, 54, 73, 73, + 73, 73, 149, 80, 55, 81, 383, 65, 98, 75, + 150, 74, 68, 82, 83, 84, 88, 69, 89, 74, + 76, 85, 86, 90, 87, 59, 60, 61, 136, 92, + 94, 58, 382, 151, 58, 65, 381, 99, 75, 93, + 74, 76, 58, 97, 55, 152, 380, 91, 58, 95, + 379, 378, 90, 96, 377, 137, 58, 136, 376, 94, + 373, 58, 59, 60, 61, 59, 60, 61, 93, 372, + + 100, 58, 97, 59, 60, 61, 371, 370, 95, 59, + 60, 61, 96, 58, 137, 56, 369, 59, 60, 61, + 368, 367, 59, 60, 61, 102, 102, 102, 102, 101, + 366, 58, 59, 60, 61, 70, 70, 70, 70, 104, + 196, 197, 103, 364, 59, 60, 61, 105, 108, 108, + 108, 108, 238, 106, 127, 363, 265, 362, 109, 107, + 58, 282, 59, 60, 61, 58, 111, 361, 104, 196, + 197, 103, 112, 360, 110, 359, 105, 358, 58, 357, + 56, 238, 356, 58, 56, 265, 56, 109, 107, 298, + 282, 59, 60, 61, 99, 113, 59, 60, 61, 91, + + 114, 112, 115, 110, 90, 116, 116, 116, 116, 59, + 60, 61, 58, 93, 59, 60, 61, 101, 298, 312, + 58, 354, 353, 122, 352, 351, 120, 350, 121, 103, + 58, 117, 349, 90, 348, 58, 140, 140, 140, 140, + 58, 347, 93, 59, 60, 61, 100, 58, 312, 346, + 344, 59, 60, 61, 342, 58, 104, 341, 103, 337, + 118, 59, 60, 61, 105, 109, 59, 60, 61, 336, + 332, 59, 60, 61, 331, 327, 107, 326, 59, 60, + 61, 110, 58, 113, 320, 104, 59, 60, 61, 112, + 58, 118, 319, 105, 109, 58, 311, 310, 297, 123, + + 58, 124, 296, 63, 53, 107, 57, 57, 57, 57, + 110, 281, 125, 59, 60, 61, 58, 280, 112, 264, + 126, 59, 60, 61, 263, 261, 59, 60, 61, 237, + 236, 59, 60, 61, 53, 195, 193, 53, 144, 63, + 388, 58, 63, 375, 374, 53, 53, 59, 60, 61, + 63, 63, 365, 355, 72, 345, 67, 67, 67, 67, + 142, 284, 142, 247, 268, 143, 143, 143, 143, 74, + 267, 243, 59, 60, 61, 70, 70, 70, 70, 247, + 245, 53, 159, 244, 243, 53, 63, 162, 138, 53, + 63, 242, 155, 241, 63, 240, 56, 53, 74, 76, + + 58, 53, 63, 53, 132, 158, 63, 58, 63, 135, + 141, 141, 141, 141, 129, 58, 162, 138, 139, 210, + 156, 71, 72, 138, 73, 73, 73, 73, 156, 58, + 209, 59, 60, 61, 158, 208, 207, 74, 59, 60, + 61, 157, 206, 58, 205, 204, 59, 60, 61, 156, + 160, 58, 138, 139, 203, 202, 163, 156, 165, 201, + 59, 60, 61, 161, 58, 164, 74, 76, 194, 168, + 58, 166, 56, 58, 59, 60, 61, 192, 167, 160, + 129, 58, 59, 60, 61, 163, 58, 165, 185, 154, + 58, 153, 161, 58, 164, 59, 60, 61, 168, 168, + + 167, 59, 60, 61, 59, 60, 61, 167, 181, 181, + 181, 181, 59, 60, 61, 148, 147, 59, 60, 61, + 58, 59, 60, 61, 59, 60, 61, 172, 168, 102, + 102, 102, 102, 169, 170, 146, 171, 145, 134, 173, + 58, 133, 108, 108, 108, 108, 56, 58, 51, 131, + 58, 59, 60, 61, 58, 176, 172, 130, 191, 129, + 128, 174, 119, 170, 58, 171, 79, 175, 173, 62, + 58, 59, 60, 61, 58, 177, 52, 58, 59, 60, + 61, 59, 60, 61, 176, 59, 60, 61, 51, 179, + 389, 180, 58, 389, 389, 59, 60, 61, 58, 389, + + 389, 59, 60, 61, 177, 59, 60, 61, 59, 60, + 61, 389, 178, 183, 389, 389, 58, 179, 179, 58, + 180, 389, 389, 59, 60, 61, 58, 389, 389, 59, + 60, 61, 116, 116, 116, 116, 116, 116, 116, 116, + 182, 58, 184, 389, 389, 389, 179, 59, 60, 61, + 59, 60, 61, 389, 184, 155, 156, 59, 60, 61, + 58, 167, 389, 389, 58, 389, 389, 58, 389, 389, + 389, 58, 59, 60, 61, 389, 58, 389, 389, 58, + 187, 389, 58, 184, 389, 156, 175, 58, 188, 389, + 186, 59, 60, 61, 389, 59, 60, 61, 59, 60, + + 61, 389, 59, 60, 61, 389, 157, 59, 60, 61, + 59, 60, 61, 59, 60, 61, 179, 389, 59, 60, + 61, 389, 184, 389, 198, 58, 198, 389, 58, 199, + 199, 199, 199, 200, 212, 58, 140, 140, 140, 140, + 189, 141, 141, 141, 141, 179, 143, 143, 143, 143, + 389, 190, 389, 211, 138, 58, 59, 60, 61, 59, + 60, 61, 143, 143, 143, 143, 59, 60, 61, 58, + 222, 222, 222, 222, 213, 58, 181, 181, 181, 181, + 182, 58, 211, 138, 139, 389, 59, 60, 61, 389, + 389, 214, 58, 216, 389, 58, 199, 199, 199, 199, + + 59, 60, 61, 213, 215, 76, 59, 60, 61, 389, + 389, 58, 59, 60, 61, 389, 389, 58, 389, 389, + 214, 389, 216, 59, 60, 61, 59, 60, 61, 217, + 389, 218, 389, 215, 389, 223, 58, 219, 389, 58, + 389, 389, 59, 60, 61, 58, 220, 389, 59, 60, + 61, 389, 389, 389, 58, 221, 389, 58, 217, 389, + 218, 58, 224, 389, 223, 58, 220, 59, 60, 61, + 59, 60, 61, 58, 389, 220, 59, 60, 61, 225, + 58, 389, 389, 58, 221, 59, 60, 61, 59, 60, + 61, 224, 59, 60, 61, 226, 59, 60, 61, 157, + + 389, 58, 389, 389, 59, 60, 61, 58, 389, 389, + 58, 59, 60, 61, 59, 60, 61, 389, 227, 228, + 389, 58, 231, 389, 226, 58, 389, 389, 389, 58, + 389, 389, 59, 60, 61, 234, 389, 232, 59, 60, + 61, 59, 60, 61, 58, 389, 389, 227, 228, 389, + 389, 232, 59, 60, 61, 389, 59, 60, 61, 58, + 59, 60, 61, 229, 220, 229, 232, 58, 230, 230, + 230, 230, 58, 232, 225, 59, 60, 61, 58, 389, + 389, 58, 199, 199, 199, 199, 389, 389, 157, 389, + 59, 60, 61, 233, 389, 58, 389, 389, 59, 60, + + 61, 58, 231, 59, 60, 61, 246, 235, 58, 59, + 60, 61, 59, 60, 61, 239, 239, 239, 239, 58, + 250, 389, 389, 389, 249, 139, 59, 60, 61, 251, + 253, 58, 59, 60, 61, 246, 389, 58, 252, 59, + 60, 61, 58, 222, 222, 222, 222, 389, 58, 250, + 59, 60, 61, 249, 255, 58, 389, 389, 251, 253, + 58, 254, 59, 60, 61, 58, 389, 252, 59, 60, + 61, 257, 256, 59, 60, 61, 58, 389, 389, 59, + 60, 61, 258, 256, 58, 262, 59, 60, 61, 58, + 254, 59, 60, 61, 58, 389, 59, 60, 61, 58, + + 257, 256, 58, 230, 230, 230, 230, 59, 60, 61, + 389, 258, 259, 260, 389, 59, 60, 61, 58, 389, + 59, 60, 61, 256, 58, 59, 60, 61, 58, 260, + 59, 60, 61, 59, 60, 61, 230, 230, 230, 230, + 58, 259, 260, 389, 389, 58, 269, 389, 389, 59, + 60, 61, 255, 389, 389, 59, 60, 61, 260, 59, + 60, 61, 239, 239, 239, 239, 270, 271, 272, 58, + 266, 59, 60, 61, 58, 269, 59, 60, 61, 273, + 58, 283, 283, 283, 283, 274, 58, 389, 389, 58, + 266, 389, 58, 389, 389, 270, 271, 272, 58, 276, + + 59, 60, 61, 389, 276, 59, 60, 61, 273, 275, + 58, 59, 60, 61, 274, 58, 277, 59, 60, 61, + 59, 60, 61, 59, 60, 61, 389, 58, 276, 59, + 60, 61, 278, 276, 58, 279, 389, 389, 275, 285, + 389, 59, 60, 61, 58, 277, 59, 60, 61, 287, + 313, 313, 313, 313, 58, 290, 389, 58, 59, 60, + 61, 278, 58, 389, 279, 59, 60, 61, 285, 286, + 288, 389, 389, 58, 289, 59, 60, 61, 287, 58, + 389, 389, 389, 58, 290, 59, 60, 61, 59, 60, + 61, 389, 292, 59, 60, 61, 389, 58, 286, 288, + + 389, 58, 291, 289, 59, 60, 61, 389, 389, 58, + 59, 60, 61, 389, 59, 60, 61, 293, 294, 389, + 389, 292, 295, 389, 58, 389, 389, 58, 59, 60, + 61, 291, 59, 60, 61, 283, 283, 283, 283, 299, + 59, 60, 61, 389, 58, 389, 293, 294, 389, 300, + 301, 295, 58, 389, 389, 59, 60, 61, 59, 60, + 61, 305, 58, 302, 389, 389, 389, 304, 58, 303, + 389, 58, 389, 389, 58, 59, 60, 61, 300, 301, + 58, 389, 389, 59, 60, 61, 306, 58, 389, 389, + 305, 389, 302, 59, 60, 61, 304, 389, 303, 59, + + 60, 61, 59, 60, 61, 59, 60, 61, 58, 309, + 389, 59, 60, 61, 307, 306, 58, 314, 59, 60, + 61, 308, 315, 58, 389, 389, 58, 389, 389, 58, + 389, 389, 58, 328, 328, 328, 328, 58, 309, 59, + 60, 61, 58, 307, 389, 58, 314, 59, 60, 61, + 308, 315, 58, 316, 59, 60, 61, 59, 60, 61, + 59, 60, 61, 59, 60, 61, 58, 317, 59, 60, + 61, 58, 389, 59, 60, 61, 59, 60, 61, 58, + 323, 389, 316, 59, 60, 61, 389, 318, 389, 322, + 58, 313, 313, 313, 313, 321, 317, 59, 60, 61, + + 58, 324, 59, 60, 61, 58, 389, 389, 58, 323, + 59, 60, 61, 389, 58, 389, 318, 389, 322, 58, + 325, 59, 60, 61, 58, 338, 338, 338, 338, 329, + 324, 59, 60, 61, 330, 389, 59, 60, 61, 59, + 60, 61, 389, 334, 58, 59, 60, 61, 389, 325, + 59, 60, 61, 58, 389, 59, 60, 61, 329, 389, + 58, 389, 389, 330, 328, 328, 328, 328, 333, 58, + 389, 389, 334, 389, 335, 59, 60, 61, 389, 389, + 339, 58, 340, 389, 59, 60, 61, 58, 343, 389, + 58, 59, 60, 61, 338, 338, 338, 338, 58, 389, + + 59, 60, 61, 335, 58, 389, 389, 389, 389, 339, + 58, 340, 59, 60, 61, 389, 389, 343, 59, 60, + 61, 59, 60, 61, 389, 389, 389, 389, 389, 59, + 60, 61, 389, 389, 389, 59, 60, 61, 389, 389, + 389, 59, 60, 61, 53, 53, 53, 53, 53, 56, + 56, 56, 63, 63, 63, 63, 63, 248, 389, 248, + 248, 3, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 389 } ; static const flex_int16_t yy_chk[1849] = @@ -926,204 +926,204 @@ static const flex_int16_t yy_chk[1849] = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 5, 8, 5, 9, 9, 9, - 9, 11, 14, 68, 14, 14, 14, 14, 15, 15, - - 15, 15, 16, 16, 456, 16, 16, 16, 16, 18, - 18, 14, 19, 19, 19, 50, 14, 50, 16, 23, - 68, 9, 17, 17, 28, 17, 17, 17, 17, 23, - 455, 8, 63, 84, 11, 136, 16, 53, 17, 14, - 454, 84, 453, 14, 452, 451, 16, 16, 450, 449, - 23, 9, 9, 9, 28, 28, 28, 23, 22, 21, - 22, 21, 136, 56, 16, 22, 17, 17, 21, 21, - 21, 27, 22, 24, 448, 63, 21, 21, 26, 21, - 23, 23, 23, 53, 24, 25, 447, 22, 446, 25, - 26, 69, 22, 56, 56, 56, 27, 29, 445, 27, - - 24, 85, 22, 22, 22, 26, 25, 135, 444, 29, - 58, 443, 85, 25, 24, 24, 24, 25, 236, 69, - 26, 26, 26, 442, 29, 194, 27, 27, 27, 30, - 30, 30, 30, 280, 31, 135, 25, 25, 25, 29, - 29, 29, 31, 263, 296, 236, 30, 441, 31, 32, - 32, 32, 32, 194, 31, 33, 440, 37, 439, 31, - 280, 33, 31, 30, 438, 437, 33, 436, 435, 434, - 31, 263, 296, 58, 30, 32, 433, 58, 37, 58, - 432, 31, 33, 32, 431, 37, 430, 429, 33, 31, - 31, 31, 428, 30, 30, 30, 33, 33, 33, 34, - - 427, 34, 32, 38, 34, 34, 34, 34, 37, 37, - 37, 45, 36, 32, 32, 32, 426, 36, 40, 425, - 39, 424, 423, 422, 36, 421, 420, 419, 38, 418, - 34, 38, 39, 45, 417, 40, 416, 36, 34, 36, - 415, 45, 45, 45, 36, 109, 40, 39, 66, 66, - 66, 66, 414, 41, 36, 36, 36, 34, 38, 38, - 38, 41, 39, 39, 39, 40, 40, 40, 34, 34, - 34, 413, 412, 41, 42, 109, 109, 109, 41, 411, - 44, 41, 42, 410, 43, 409, 408, 407, 44, 41, - 43, 406, 405, 42, 41, 43, 57, 57, 57, 57, - - 41, 42, 404, 70, 70, 70, 70, 44, 41, 41, - 41, 43, 42, 42, 42, 403, 70, 43, 44, 44, - 44, 55, 402, 401, 55, 43, 43, 43, 400, 399, - 57, 65, 55, 55, 65, 67, 398, 67, 67, 67, - 67, 397, 65, 65, 70, 70, 71, 71, 71, 71, - 67, 395, 72, 72, 72, 72, 113, 113, 113, 113, - 57, 57, 57, 394, 393, 72, 392, 55, 391, 385, - 371, 55, 370, 88, 361, 55, 351, 65, 67, 67, - 340, 65, 55, 266, 246, 65, 55, 88, 55, 55, - 243, 240, 65, 72, 72, 92, 65, 90, 65, 65, - - 73, 73, 89, 73, 73, 73, 73, 74, 238, 74, - 91, 210, 74, 74, 74, 74, 73, 88, 88, 88, - 90, 93, 89, 208, 89, 90, 207, 92, 205, 202, - 89, 201, 91, 200, 93, 192, 190, 153, 91, 95, - 93, 94, 152, 151, 73, 73, 95, 150, 149, 93, - 90, 90, 90, 94, 89, 89, 89, 92, 92, 92, - 96, 93, 91, 91, 91, 98, 95, 96, 94, 148, - 93, 93, 93, 97, 147, 99, 95, 95, 95, 98, - 100, 146, 145, 94, 94, 94, 101, 96, 103, 99, - 103, 144, 98, 133, 132, 97, 130, 96, 96, 96, - - 100, 97, 99, 103, 129, 118, 101, 87, 100, 98, - 98, 98, 86, 83, 101, 103, 82, 103, 81, 99, - 99, 99, 80, 61, 60, 97, 97, 97, 59, 51, - 100, 100, 100, 103, 103, 103, 101, 101, 101, 102, - 102, 102, 102, 102, 104, 105, 108, 108, 108, 108, - 49, 106, 107, 111, 48, 47, 104, 106, 46, 105, - 35, 20, 10, 106, 7, 6, 107, 3, 0, 0, - 110, 104, 105, 102, 0, 110, 0, 111, 0, 107, - 108, 111, 0, 110, 0, 0, 104, 104, 104, 105, - 105, 105, 112, 106, 106, 106, 107, 107, 107, 0, - - 112, 0, 110, 102, 102, 102, 0, 111, 111, 111, - 108, 108, 108, 110, 110, 110, 128, 0, 0, 112, - 114, 114, 114, 114, 115, 115, 115, 115, 115, 116, - 112, 112, 112, 117, 116, 121, 0, 0, 117, 120, - 0, 0, 119, 0, 0, 123, 128, 128, 128, 121, - 0, 123, 0, 0, 114, 119, 116, 0, 115, 0, - 117, 120, 121, 0, 116, 116, 116, 120, 117, 117, - 117, 0, 119, 119, 119, 123, 123, 123, 0, 121, - 121, 121, 122, 0, 114, 114, 114, 120, 115, 115, - 115, 120, 120, 120, 124, 0, 0, 122, 126, 0, - - 125, 0, 124, 122, 0, 125, 0, 0, 0, 126, - 0, 0, 122, 122, 122, 0, 124, 137, 0, 137, - 158, 124, 137, 137, 137, 137, 0, 125, 126, 126, - 126, 154, 124, 124, 124, 125, 125, 125, 139, 155, - 0, 139, 139, 139, 139, 140, 140, 140, 140, 141, - 141, 141, 141, 142, 142, 142, 142, 157, 140, 156, - 0, 154, 154, 154, 159, 0, 158, 0, 0, 155, - 155, 155, 168, 168, 168, 168, 0, 162, 157, 0, - 0, 159, 0, 0, 0, 157, 140, 140, 161, 156, - 156, 156, 159, 0, 161, 142, 158, 158, 158, 160, - - 162, 163, 0, 160, 0, 162, 0, 163, 157, 157, - 157, 159, 159, 159, 164, 161, 171, 196, 196, 196, - 196, 164, 165, 0, 161, 161, 161, 160, 163, 165, - 162, 162, 162, 160, 160, 160, 166, 163, 163, 163, - 167, 164, 0, 166, 169, 0, 171, 171, 171, 165, - 0, 164, 164, 164, 170, 173, 0, 167, 0, 165, - 165, 165, 172, 166, 176, 169, 0, 0, 167, 0, - 0, 170, 169, 166, 166, 166, 173, 175, 0, 174, - 0, 0, 170, 0, 0, 176, 0, 167, 167, 167, - 0, 175, 172, 172, 172, 169, 169, 169, 0, 177, - - 186, 170, 170, 170, 175, 177, 173, 173, 173, 174, - 174, 174, 181, 0, 0, 176, 176, 176, 178, 0, - 0, 175, 175, 175, 0, 0, 177, 178, 179, 179, - 179, 179, 179, 181, 182, 177, 177, 177, 0, 0, - 181, 180, 186, 180, 184, 178, 180, 180, 180, 180, - 0, 184, 0, 0, 0, 182, 188, 178, 178, 178, - 185, 0, 182, 181, 181, 181, 0, 185, 187, 0, - 0, 184, 186, 186, 186, 187, 0, 188, 0, 0, - 0, 184, 184, 184, 188, 182, 182, 182, 189, 0, - 185, 185, 185, 0, 189, 0, 213, 0, 187, 187, - - 187, 197, 197, 197, 197, 209, 0, 188, 188, 188, - 198, 198, 198, 198, 211, 0, 212, 209, 189, 189, - 189, 213, 0, 0, 213, 0, 211, 220, 220, 220, - 220, 0, 209, 214, 0, 0, 0, 212, 0, 0, - 0, 211, 215, 197, 212, 217, 216, 209, 209, 209, - 216, 213, 213, 213, 214, 218, 211, 211, 211, 215, - 219, 214, 221, 0, 0, 0, 217, 212, 212, 212, - 215, 0, 219, 217, 216, 0, 218, 0, 0, 223, - 216, 216, 216, 218, 214, 214, 214, 219, 224, 215, - 215, 215, 221, 221, 221, 222, 217, 217, 217, 233, - - 0, 222, 219, 219, 219, 225, 218, 218, 218, 223, - 223, 223, 226, 227, 227, 227, 227, 226, 224, 224, - 224, 0, 222, 228, 228, 228, 228, 0, 229, 231, - 0, 222, 222, 222, 230, 225, 225, 225, 229, 226, - 0, 233, 0, 0, 230, 0, 0, 226, 226, 226, - 231, 237, 237, 237, 237, 229, 244, 231, 247, 237, - 0, 230, 248, 0, 0, 247, 0, 0, 229, 229, - 229, 233, 233, 233, 230, 230, 230, 249, 244, 237, - 231, 231, 231, 248, 244, 247, 0, 0, 0, 250, - 248, 0, 251, 0, 249, 247, 247, 247, 0, 0, - - 252, 250, 253, 0, 251, 249, 0, 0, 244, 244, - 244, 254, 253, 248, 248, 248, 250, 252, 0, 251, - 0, 254, 255, 0, 249, 249, 249, 256, 252, 253, - 0, 250, 250, 250, 251, 251, 251, 258, 254, 255, - 257, 0, 253, 253, 253, 258, 0, 252, 252, 252, - 255, 254, 254, 254, 0, 267, 285, 256, 256, 256, - 0, 257, 268, 0, 258, 0, 268, 271, 257, 255, - 255, 255, 267, 269, 0, 258, 258, 258, 264, 264, - 264, 264, 270, 267, 272, 269, 285, 285, 285, 271, - 268, 257, 257, 257, 270, 271, 268, 268, 268, 0, - - 269, 0, 267, 267, 267, 273, 274, 272, 0, 270, - 0, 273, 272, 0, 274, 269, 269, 269, 0, 271, - 271, 271, 275, 276, 270, 270, 270, 277, 275, 0, - 0, 276, 273, 274, 0, 0, 0, 272, 272, 272, - 0, 273, 273, 273, 274, 274, 274, 0, 277, 275, - 276, 297, 297, 297, 297, 277, 0, 283, 275, 275, - 275, 276, 276, 276, 281, 281, 281, 281, 281, 283, - 284, 319, 319, 319, 319, 286, 287, 0, 277, 277, - 277, 0, 286, 0, 283, 0, 0, 284, 289, 331, - 331, 331, 331, 287, 290, 0, 0, 0, 284, 283, - - 283, 283, 286, 288, 287, 0, 289, 0, 0, 288, - 0, 0, 286, 286, 286, 290, 289, 284, 284, 284, - 293, 300, 290, 287, 287, 287, 291, 0, 291, 292, - 288, 292, 0, 0, 302, 0, 289, 289, 289, 288, - 288, 288, 293, 301, 299, 290, 290, 290, 293, 298, - 299, 300, 300, 300, 291, 301, 304, 292, 291, 291, - 291, 292, 292, 292, 302, 302, 302, 0, 298, 0, - 301, 299, 293, 293, 293, 303, 307, 298, 304, 0, - 299, 299, 299, 305, 304, 301, 301, 301, 306, 336, - 336, 336, 336, 307, 313, 0, 0, 0, 298, 298, - - 298, 312, 0, 0, 307, 303, 303, 303, 304, 304, - 304, 0, 313, 305, 305, 305, 320, 314, 306, 306, - 306, 0, 313, 307, 307, 307, 311, 311, 311, 311, - 311, 312, 312, 312, 315, 316, 314, 0, 0, 321, - 0, 0, 313, 313, 313, 314, 320, 320, 320, 322, - 323, 315, 326, 326, 326, 326, 326, 0, 316, 327, - 0, 0, 315, 316, 338, 0, 314, 314, 314, 321, - 321, 321, 322, 328, 0, 323, 0, 322, 323, 328, - 333, 315, 315, 315, 327, 332, 0, 327, 316, 316, - 316, 332, 337, 0, 338, 338, 338, 341, 0, 0, - - 328, 333, 322, 322, 322, 323, 323, 323, 333, 328, - 328, 328, 332, 337, 327, 327, 327, 0, 0, 0, - 337, 332, 332, 332, 0, 0, 0, 341, 341, 341, - 0, 333, 333, 333, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 337, 337, 337, 388, 388, 388, 388, - 388, 389, 389, 389, 390, 390, 390, 390, 390, 396, - 0, 396, 396, 387, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, - - 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 387, 387, 387, 387, 387, 387, 387, - 387, 387, 387, 387, 387, 387, 387, 387 + 1, 1, 1, 1, 1, 1, 5, 8, 5, 9, + 9, 9, 9, 11, 14, 458, 14, 14, 14, 14, + + 15, 15, 15, 15, 16, 16, 457, 16, 16, 16, + 16, 18, 18, 14, 19, 19, 19, 50, 14, 50, + 16, 63, 456, 455, 9, 17, 17, 53, 17, 17, + 17, 17, 84, 21, 8, 21, 454, 11, 27, 16, + 84, 17, 14, 21, 21, 21, 22, 14, 22, 16, + 16, 21, 21, 22, 21, 9, 9, 9, 68, 23, + 24, 22, 453, 85, 27, 63, 452, 27, 16, 23, + 17, 17, 24, 26, 53, 85, 451, 22, 28, 25, + 450, 449, 22, 25, 448, 69, 26, 68, 447, 24, + 446, 23, 22, 22, 22, 27, 27, 27, 23, 445, + + 29, 25, 26, 24, 24, 24, 444, 443, 25, 28, + 28, 28, 25, 29, 69, 58, 442, 26, 26, 26, + 441, 440, 23, 23, 23, 30, 30, 30, 30, 29, + 439, 45, 25, 25, 25, 66, 66, 66, 66, 31, + 136, 137, 30, 438, 29, 29, 29, 31, 32, 32, + 32, 32, 196, 31, 45, 437, 238, 436, 32, 31, + 30, 265, 45, 45, 45, 31, 33, 435, 31, 136, + 137, 30, 33, 434, 32, 433, 31, 432, 33, 431, + 58, 196, 430, 32, 58, 238, 58, 32, 31, 282, + 265, 30, 30, 30, 38, 33, 31, 31, 31, 36, + + 34, 33, 34, 32, 36, 34, 34, 34, 34, 33, + 33, 33, 36, 37, 32, 32, 32, 39, 282, 298, + 38, 429, 428, 38, 427, 426, 36, 425, 36, 40, + 39, 34, 424, 36, 423, 37, 71, 71, 71, 71, + 34, 422, 37, 36, 36, 36, 39, 40, 298, 421, + 420, 38, 38, 38, 419, 56, 41, 418, 40, 417, + 34, 39, 39, 39, 41, 42, 37, 37, 37, 416, + 415, 34, 34, 34, 414, 413, 41, 412, 40, 40, + 40, 42, 41, 43, 411, 41, 56, 56, 56, 43, + 42, 44, 410, 41, 42, 43, 409, 408, 407, 41, + + 44, 42, 406, 405, 404, 41, 57, 57, 57, 57, + 42, 403, 43, 41, 41, 41, 110, 402, 43, 401, + 44, 42, 42, 42, 400, 399, 43, 43, 43, 397, + 396, 44, 44, 44, 55, 395, 394, 55, 393, 65, + 387, 57, 65, 373, 372, 55, 55, 110, 110, 110, + 65, 65, 363, 353, 67, 342, 67, 67, 67, 67, + 74, 268, 74, 248, 245, 74, 74, 74, 74, 67, + 242, 240, 57, 57, 57, 70, 70, 70, 70, 212, + 210, 55, 92, 209, 207, 55, 65, 94, 70, 55, + 65, 204, 88, 203, 65, 202, 194, 55, 67, 67, + + 94, 55, 65, 55, 55, 90, 65, 88, 65, 65, + 72, 72, 72, 72, 192, 92, 94, 70, 70, 154, + 89, 73, 73, 72, 73, 73, 73, 73, 91, 90, + 153, 94, 94, 94, 90, 152, 151, 73, 88, 88, + 88, 89, 150, 89, 149, 148, 92, 92, 92, 89, + 93, 91, 72, 72, 147, 146, 95, 91, 97, 145, + 90, 90, 90, 93, 95, 96, 73, 73, 134, 100, + 93, 98, 133, 96, 89, 89, 89, 131, 99, 93, + 130, 97, 91, 91, 91, 95, 98, 97, 119, 87, + 100, 86, 93, 99, 96, 95, 95, 95, 100, 101, + + 98, 93, 93, 93, 96, 96, 96, 99, 114, 114, + 114, 114, 97, 97, 97, 83, 82, 98, 98, 98, + 101, 100, 100, 100, 99, 99, 99, 104, 101, 102, + 102, 102, 102, 102, 103, 81, 103, 80, 61, 105, + 104, 60, 108, 108, 108, 108, 59, 127, 51, 49, + 103, 101, 101, 101, 105, 107, 104, 48, 127, 47, + 46, 106, 35, 103, 102, 103, 20, 106, 105, 10, + 107, 104, 104, 104, 106, 109, 7, 108, 127, 127, + 127, 103, 103, 103, 107, 105, 105, 105, 6, 113, + 3, 112, 109, 0, 0, 102, 102, 102, 113, 0, + + 0, 107, 107, 107, 109, 106, 106, 106, 108, 108, + 108, 0, 111, 117, 0, 0, 112, 111, 113, 117, + 112, 0, 0, 109, 109, 109, 111, 0, 0, 113, + 113, 113, 115, 115, 115, 115, 116, 116, 116, 116, + 116, 120, 117, 0, 0, 0, 111, 112, 112, 112, + 117, 117, 117, 0, 118, 120, 121, 111, 111, 111, + 118, 122, 0, 0, 123, 0, 0, 115, 0, 0, + 0, 116, 120, 120, 120, 0, 122, 0, 0, 121, + 123, 0, 124, 118, 0, 121, 123, 129, 124, 0, + 122, 118, 118, 118, 0, 123, 123, 123, 115, 115, + + 115, 0, 116, 116, 116, 0, 121, 122, 122, 122, + 121, 121, 121, 124, 124, 124, 125, 0, 129, 129, + 129, 0, 126, 0, 138, 125, 138, 0, 126, 138, + 138, 138, 138, 140, 159, 155, 140, 140, 140, 140, + 125, 141, 141, 141, 141, 125, 142, 142, 142, 142, + 0, 126, 0, 158, 141, 156, 125, 125, 125, 126, + 126, 126, 143, 143, 143, 143, 155, 155, 155, 157, + 169, 169, 169, 169, 160, 158, 181, 181, 181, 181, + 181, 159, 158, 141, 141, 0, 156, 156, 156, 0, + 0, 161, 160, 163, 0, 161, 198, 198, 198, 198, + + 157, 157, 157, 160, 162, 143, 158, 158, 158, 0, + 0, 162, 159, 159, 159, 0, 0, 163, 0, 0, + 161, 0, 163, 160, 160, 160, 161, 161, 161, 164, + 0, 165, 0, 162, 0, 170, 164, 166, 0, 165, + 0, 0, 162, 162, 162, 166, 167, 0, 163, 163, + 163, 0, 0, 0, 167, 168, 0, 170, 164, 0, + 165, 172, 171, 0, 170, 173, 166, 164, 164, 164, + 165, 165, 165, 168, 0, 167, 166, 166, 166, 174, + 171, 0, 0, 175, 168, 167, 167, 167, 170, 170, + 170, 171, 172, 172, 172, 176, 173, 173, 173, 178, + + 0, 174, 0, 0, 168, 168, 168, 177, 0, 0, + 176, 171, 171, 171, 175, 175, 175, 0, 179, 180, + 0, 178, 183, 0, 176, 179, 0, 0, 0, 180, + 0, 0, 174, 174, 174, 188, 0, 184, 177, 177, + 177, 176, 176, 176, 183, 0, 0, 179, 180, 0, + 0, 183, 178, 178, 178, 0, 179, 179, 179, 184, + 180, 180, 180, 182, 186, 182, 184, 187, 182, 182, + 182, 182, 186, 190, 187, 183, 183, 183, 188, 0, + 0, 189, 199, 199, 199, 199, 0, 0, 189, 0, + 184, 184, 184, 186, 0, 190, 0, 0, 187, 187, + + 187, 191, 190, 186, 186, 186, 211, 191, 223, 188, + 188, 188, 189, 189, 189, 200, 200, 200, 200, 211, + 214, 0, 0, 0, 213, 199, 190, 190, 190, 215, + 217, 225, 191, 191, 191, 211, 0, 213, 216, 223, + 223, 223, 214, 222, 222, 222, 222, 0, 217, 214, + 211, 211, 211, 213, 219, 215, 0, 0, 215, 217, + 216, 218, 225, 225, 225, 218, 0, 216, 213, 213, + 213, 221, 220, 214, 214, 214, 219, 0, 0, 217, + 217, 217, 224, 219, 221, 235, 215, 215, 215, 224, + 218, 216, 216, 216, 220, 0, 218, 218, 218, 226, + + 221, 220, 227, 229, 229, 229, 229, 219, 219, 219, + 0, 224, 228, 231, 0, 221, 221, 221, 228, 0, + 224, 224, 224, 233, 231, 220, 220, 220, 235, 232, + 226, 226, 226, 227, 227, 227, 230, 230, 230, 230, + 232, 228, 231, 0, 0, 233, 246, 0, 0, 228, + 228, 228, 233, 0, 0, 231, 231, 231, 232, 235, + 235, 235, 239, 239, 239, 239, 249, 250, 251, 246, + 239, 232, 232, 232, 249, 246, 233, 233, 233, 252, + 258, 266, 266, 266, 266, 253, 251, 0, 0, 250, + 239, 0, 252, 0, 0, 249, 250, 251, 253, 255, + + 246, 246, 246, 0, 256, 249, 249, 249, 252, 254, + 255, 258, 258, 258, 253, 256, 257, 251, 251, 251, + 250, 250, 250, 252, 252, 252, 0, 254, 255, 253, + 253, 253, 259, 256, 257, 260, 0, 0, 254, 269, + 0, 255, 255, 255, 260, 257, 256, 256, 256, 271, + 299, 299, 299, 299, 259, 274, 0, 269, 254, 254, + 254, 259, 271, 0, 260, 257, 257, 257, 269, 270, + 272, 0, 0, 270, 273, 260, 260, 260, 271, 274, + 0, 0, 0, 272, 274, 259, 259, 259, 269, 269, + 269, 0, 276, 271, 271, 271, 0, 273, 270, 272, + + 0, 276, 275, 273, 270, 270, 270, 0, 0, 275, + 274, 274, 274, 0, 272, 272, 272, 277, 278, 0, + 0, 276, 279, 0, 277, 0, 0, 278, 273, 273, + 273, 275, 276, 276, 276, 283, 283, 283, 283, 283, + 275, 275, 275, 0, 279, 0, 277, 278, 0, 285, + 286, 279, 287, 0, 0, 277, 277, 277, 278, 278, + 278, 291, 285, 288, 0, 0, 0, 290, 286, 289, + 0, 288, 0, 0, 290, 279, 279, 279, 285, 286, + 291, 0, 0, 287, 287, 287, 292, 289, 0, 0, + 291, 0, 288, 285, 285, 285, 290, 0, 289, 286, + + 286, 286, 288, 288, 288, 290, 290, 290, 292, 295, + 0, 291, 291, 291, 293, 292, 293, 300, 289, 289, + 289, 294, 301, 294, 0, 0, 302, 0, 0, 301, + 0, 0, 295, 321, 321, 321, 321, 300, 295, 292, + 292, 292, 304, 293, 0, 305, 300, 293, 293, 293, + 294, 301, 307, 303, 294, 294, 294, 302, 302, 302, + 301, 301, 301, 295, 295, 295, 303, 306, 300, 300, + 300, 308, 0, 304, 304, 304, 305, 305, 305, 314, + 316, 0, 303, 307, 307, 307, 0, 309, 0, 315, + 306, 313, 313, 313, 313, 313, 306, 303, 303, 303, + + 316, 317, 308, 308, 308, 309, 0, 0, 315, 316, + 314, 314, 314, 0, 322, 0, 309, 0, 315, 317, + 318, 306, 306, 306, 323, 333, 333, 333, 333, 324, + 317, 316, 316, 316, 325, 0, 309, 309, 309, 315, + 315, 315, 0, 329, 318, 322, 322, 322, 0, 318, + 317, 317, 317, 324, 0, 323, 323, 323, 324, 0, + 325, 0, 0, 325, 328, 328, 328, 328, 328, 329, + 0, 0, 329, 0, 330, 318, 318, 318, 0, 0, + 334, 330, 335, 0, 324, 324, 324, 334, 339, 0, + 340, 325, 325, 325, 338, 338, 338, 338, 343, 0, + + 329, 329, 329, 330, 335, 0, 0, 0, 0, 334, + 339, 335, 330, 330, 330, 0, 0, 339, 334, 334, + 334, 340, 340, 340, 0, 0, 0, 0, 0, 343, + 343, 343, 0, 0, 0, 335, 335, 335, 0, 0, + 0, 339, 339, 339, 390, 390, 390, 390, 390, 391, + 391, 391, 392, 392, 392, 392, 392, 398, 0, 398, + 398, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, + 389, 389, 389, 389, 389, 389, 389, 389 } ; -static const flex_int16_t yy_rule_linenum[62] = +static const flex_int16_t yy_rule_linenum[63] = { 0, 32, 33, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, @@ -1131,7 +1131,7 @@ static const flex_int16_t yy_rule_linenum[62] = 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, - 94 + 93, 95 } ; /* The intent behind this definition is that it'll catch @@ -1599,13 +1599,13 @@ YY_DECL while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 388 ) + if ( yy_current_state >= 390 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; ++yy_cp; } - while ( yy_current_state != 387 ); + while ( yy_current_state != 389 ); yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; @@ -1625,13 +1625,13 @@ YY_DECL { if ( yy_act == 0 ) fprintf( stderr, "--scanner backing up\n" ); - else if ( yy_act < 62 ) + else if ( yy_act < 63 ) fprintf( stderr, "--accepting rule at line %ld (\"%s\")\n", (long)yy_rule_linenum[yy_act], yytext ); - else if ( yy_act == 62 ) + else if ( yy_act == 63 ) fprintf( stderr, "--accepting default rule (\"%s\")\n", yytext ); - else if ( yy_act == 63 ) + else if ( yy_act == 64 ) fprintf( stderr, "--(end of buffer or a NUL)\n" ); else fprintf( stderr, "--EOF (start condition %d)\n", YY_START ); @@ -1758,47 +1758,47 @@ return yy::parser::make_LIMIT(yytext); YY_BREAK case 28: YY_RULE_SETUP -return yy::parser::make_ASCENDING(yytext); +return yy::parser::make_OBJ(yytext); YY_BREAK case 29: YY_RULE_SETUP -return yy::parser::make_DESCENDING(yytext); +return yy::parser::make_ASCENDING(yytext); YY_BREAK case 30: YY_RULE_SETUP -return yy::parser::make_SUBQUERY(); +return yy::parser::make_DESCENDING(yytext); YY_BREAK case 31: YY_RULE_SETUP -return yy::parser::make_SIZE(yytext); +return yy::parser::make_SUBQUERY(); YY_BREAK case 32: YY_RULE_SETUP -return yy::parser::make_MAX (); +return yy::parser::make_SIZE(yytext); YY_BREAK case 33: YY_RULE_SETUP -return yy::parser::make_MIN (); +return yy::parser::make_MAX (); YY_BREAK case 34: YY_RULE_SETUP -return yy::parser::make_SUM (); +return yy::parser::make_MIN (); YY_BREAK case 35: YY_RULE_SETUP -return yy::parser::make_AVG (); +return yy::parser::make_SUM (); YY_BREAK case 36: YY_RULE_SETUP -return yy::parser::make_BACKLINK(); +return yy::parser::make_AVG (); YY_BREAK case 37: YY_RULE_SETUP -return yy::parser::make_TYPE (yytext); +return yy::parser::make_BACKLINK(); YY_BREAK case 38: YY_RULE_SETUP -return yy::parser::make_KEY_VAL (yytext); +return yy::parser::make_TYPE (yytext); YY_BREAK case 39: YY_RULE_SETUP @@ -1806,59 +1806,59 @@ return yy::parser::make_KEY_VAL (yytext); YY_BREAK case 40: YY_RULE_SETUP -return yy::parser::make_CASE (); +return yy::parser::make_KEY_VAL (yytext); YY_BREAK case 41: YY_RULE_SETUP -return yy::parser::make_TRUE (); +return yy::parser::make_CASE (); YY_BREAK case 42: YY_RULE_SETUP -return yy::parser::make_FALSE (); +return yy::parser::make_TRUE (); YY_BREAK case 43: YY_RULE_SETUP -return yy::parser::make_INFINITY(yytext); +return yy::parser::make_FALSE (); YY_BREAK case 44: YY_RULE_SETUP -return yy::parser::make_NAN(yytext); +return yy::parser::make_INFINITY(yytext); YY_BREAK case 45: YY_RULE_SETUP -return yy::parser::make_NULL_VAL (); +return yy::parser::make_NAN(yytext); YY_BREAK case 46: YY_RULE_SETUP -return yy::parser::make_UUID(yytext); +return yy::parser::make_NULL_VAL (); YY_BREAK case 47: YY_RULE_SETUP -return yy::parser::make_OID(yytext); +return yy::parser::make_UUID(yytext); YY_BREAK case 48: YY_RULE_SETUP -return yy::parser::make_TIMESTAMP(yytext); +return yy::parser::make_OID(yytext); YY_BREAK case 49: YY_RULE_SETUP -return yy::parser::make_LINK (yytext); +return yy::parser::make_TIMESTAMP(yytext); YY_BREAK case 50: YY_RULE_SETUP -return yy::parser::make_TYPED_LINK (yytext); +return yy::parser::make_LINK (yytext); YY_BREAK case 51: YY_RULE_SETUP -return yy::parser::make_NATURAL0 (yytext); +return yy::parser::make_TYPED_LINK (yytext); YY_BREAK case 52: YY_RULE_SETUP -return yy::parser::make_ARG(yytext); +return yy::parser::make_NATURAL0 (yytext); YY_BREAK case 53: YY_RULE_SETUP -return yy::parser::make_NUMBER (yytext); +return yy::parser::make_ARG(yytext); YY_BREAK case 54: YY_RULE_SETUP @@ -1866,7 +1866,7 @@ return yy::parser::make_NUMBER (yytext); YY_BREAK case 55: YY_RULE_SETUP -return yy::parser::make_FLOAT (yytext); +return yy::parser::make_NUMBER (yytext); YY_BREAK case 56: YY_RULE_SETUP @@ -1874,12 +1874,11 @@ return yy::parser::make_FLOAT (yytext); YY_BREAK case 57: YY_RULE_SETUP -return yy::parser::make_BASE64(yytext); +return yy::parser::make_FLOAT (yytext); YY_BREAK case 58: -/* rule 58 can match eol */ YY_RULE_SETUP -return yy::parser::make_STRING (yytext); +return yy::parser::make_BASE64(yytext); YY_BREAK case 59: /* rule 59 can match eol */ @@ -1887,11 +1886,16 @@ YY_RULE_SETUP return yy::parser::make_STRING (yytext); YY_BREAK case 60: +/* rule 60 can match eol */ YY_RULE_SETUP -return yy::parser::make_ID (check_escapes(yytext)); +return yy::parser::make_STRING (yytext); YY_BREAK case 61: YY_RULE_SETUP +return yy::parser::make_ID (check_escapes(yytext)); + YY_BREAK +case 62: +YY_RULE_SETUP { throw yy::parser::syntax_error ("invalid character: " + std::string(yytext)); @@ -1900,7 +1904,7 @@ YY_RULE_SETUP case YY_STATE_EOF(INITIAL): return yy::parser::make_END (); YY_BREAK -case 62: +case 63: YY_RULE_SETUP ECHO; YY_BREAK @@ -2227,7 +2231,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 388 ) + if ( yy_current_state >= 390 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; @@ -2262,11 +2266,11 @@ static int yy_get_next_buffer (yyscan_t yyscanner) while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 388 ) + if ( yy_current_state >= 390 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; - yy_is_jam = (yy_current_state == 387); + yy_is_jam = (yy_current_state == 389); (void)yyg; return yy_is_jam ? 0 : yy_current_state; diff --git a/src/realm/parser/query_bison.yy b/src/realm/parser/query_bison.yy index 4bcdd055a96..ec0a7690d83 100644 --- a/src/realm/parser/query_bison.yy +++ b/src/realm/parser/query_bison.yy @@ -103,6 +103,7 @@ using namespace realm::query_parser; %token LIKE "like" %token BETWEEN "between" %token IN "in" +%token OBJ "obj" %token SORT "sort" %token DISTINCT "distinct" %token LIMIT "limit" @@ -113,7 +114,7 @@ using namespace realm::query_parser; %token KEY_VAL "key or value" %type direction %type equality relational stringop -%type constant +%type constant primary_key %type list list_content %type prop %type post_op @@ -232,16 +233,12 @@ list_content | list_content ',' constant { $1->add_element($3); $$ = $1; } constant - : NATURAL0 { $$ = drv.m_parse_nodes.create(ConstantNode::NUMBER, $1); } - | NUMBER { $$ = drv.m_parse_nodes.create(ConstantNode::NUMBER, $1); } + : primary_key { $$ = $1; } | INFINITY { $$ = drv.m_parse_nodes.create(ConstantNode::INFINITY_VAL, $1); } | NAN { $$ = drv.m_parse_nodes.create(ConstantNode::NAN_VAL, $1); } - | STRING { $$ = drv.m_parse_nodes.create(ConstantNode::STRING, $1); } | BASE64 { $$ = drv.m_parse_nodes.create(ConstantNode::BASE64, $1); } | FLOAT { $$ = drv.m_parse_nodes.create(ConstantNode::FLOAT, $1); } | TIMESTAMP { $$ = drv.m_parse_nodes.create(ConstantNode::TIMESTAMP, $1); } - | UUID { $$ = drv.m_parse_nodes.create(ConstantNode::UUID_T, $1); } - | OID { $$ = drv.m_parse_nodes.create(ConstantNode::OID, $1); } | LINK { $$ = drv.m_parse_nodes.create(ConstantNode::LINK, $1); } | TYPED_LINK { $$ = drv.m_parse_nodes.create(ConstantNode::TYPED_LINK, $1); } | TRUE { $$ = drv.m_parse_nodes.create(ConstantNode::TRUE, ""); } @@ -249,7 +246,19 @@ constant | NULL_VAL { $$ = drv.m_parse_nodes.create(ConstantNode::NULL_VAL, ""); } | ARG { $$ = drv.m_parse_nodes.create(ConstantNode::ARG, $1); } | comp_type ARG { $$ = drv.m_parse_nodes.create(ExpressionComparisonType($1), $2); } + | OBJ '(' STRING ',' primary_key ')' + { + auto tmp = $5; + tmp->add_table($3); + $$ = tmp; + } +primary_key + : NATURAL0 { $$ = drv.m_parse_nodes.create(ConstantNode::NUMBER, $1); } + | NUMBER { $$ = drv.m_parse_nodes.create(ConstantNode::NUMBER, $1); } + | STRING { $$ = drv.m_parse_nodes.create(ConstantNode::STRING, $1); } + | UUID { $$ = drv.m_parse_nodes.create(ConstantNode::UUID_T, $1); } + | OID { $$ = drv.m_parse_nodes.create(ConstantNode::OID, $1); } boolexpr : "truepredicate" { $$ = drv.m_parse_nodes.create(true); } diff --git a/src/realm/parser/query_flex.ll b/src/realm/parser/query_flex.ll index 01284915935..cb73f089f55 100644 --- a/src/realm/parser/query_flex.ll +++ b/src/realm/parser/query_flex.ll @@ -57,6 +57,7 @@ blank [ \t\r] (?i:sort) return yy::parser::make_SORT(yytext); (?i:distinct) return yy::parser::make_DISTINCT(yytext); (?i:limit) return yy::parser::make_LIMIT(yytext); +(?i:obj) return yy::parser::make_OBJ(yytext); (?i:ascending)|(?i:asc) return yy::parser::make_ASCENDING(yytext); (?i:descending)|(?i:desc) return yy::parser::make_DESCENDING(yytext); (?i:subquery) return yy::parser::make_SUBQUERY(); diff --git a/src/realm/query.cpp b/src/realm/query.cpp index dd88c46eb97..cc038c638cd 100644 --- a/src/realm/query.cpp +++ b/src/realm/query.cpp @@ -1844,11 +1844,11 @@ std::string Query::validate() const std::string Query::get_description(util::serializer::SerialisationState& state) const { std::string description; - if (root_node()) { + if (auto root = root_node()) { if (m_view) { throw SerialisationError("Serialisation of a query constrained by a view is not currently supported"); } - description = root_node()->describe_expression(state); + description = root->describe_expression(state); } else { // An empty query returns all results and one way to indicate this @@ -1874,7 +1874,7 @@ util::bind_ptr Query::get_ordering() std::string Query::get_description(const std::string& class_prefix) const { - util::serializer::SerialisationState state(class_prefix); + util::serializer::SerialisationState state(class_prefix, m_table->get_parent_group()); return get_description(state); } diff --git a/src/realm/query_engine.hpp b/src/realm/query_engine.hpp index 9a854e82367..8ca5f42d65c 100644 --- a/src/realm/query_engine.hpp +++ b/src/realm/query_engine.hpp @@ -1346,10 +1346,15 @@ class MixedNodeBase : public ParentNode { std::string describe(util::serializer::SerialisationState& state) const override { REALM_ASSERT(m_condition_column_key); + std::string value; + if (m_value.is_type(type_TypedLink)) { + value = util::serializer::print_value(m_value.get(), state.group); + } + else { + value = util::serializer::print_value(m_value); + } return state.describe_column(ParentNode::m_table, m_condition_column_key) + " " + this->describe_condition() + - " " + - (m_value_is_null ? util::serializer::print_value(realm::null()) - : util::serializer::print_value(m_value)); + " " + value; } protected: @@ -2417,8 +2422,9 @@ class LinksToNodeBase : public ParentNode { REALM_ASSERT(m_condition_column_key); if (m_target_keys.size() > 1) throw SerialisationError("Serializing a query which links to multiple objects is currently unsupported."); + ObjLink link(m_table->get_opposite_table(m_condition_column_key)->get_key(), m_target_keys[0]); return state.describe_column(ParentNode::m_table, m_condition_column_key) + " " + describe_condition() + " " + - util::serializer::print_value(m_target_keys[0]); + util::serializer::print_value(link, m_table->get_parent_group()); } protected: diff --git a/src/realm/query_expression.hpp b/src/realm/query_expression.hpp index b4f490ab573..e543c7c31b5 100644 --- a/src/realm/query_expression.hpp +++ b/src/realm/query_expression.hpp @@ -727,6 +727,11 @@ class Subexpr { return {}; } + virtual ConstTableRef get_target_table() const + { + return {}; + } + virtual DataType get_type() const = 0; virtual void evaluate(size_t index, ValueBase& destination) = 0; @@ -1217,15 +1222,37 @@ class Value : public ValueBase, public Subexpr2 { { } - std::string value_to_string(size_t ndx) const + std::string value_to_string(size_t ndx, util::serializer::SerialisationState& state) const { auto val = get(ndx); if (val.is_null()) return "NULL"; else { + static_cast(state); if constexpr (std::is_same_v) { return util::serializer::print_value(val.get_type_of_value()); } + else if constexpr (std::is_same_v) { + ObjKey obj_key = val.template get(); + if (state.target_table) { + ObjLink link(state.target_table->get_key(), obj_key); + return util::serializer::print_value(link, state.group); + } + else { + return util::serializer::print_value(obj_key); + } + } + else if constexpr (std::is_same_v) { + return util::serializer::print_value(val.template get(), state.group); + } + else if constexpr (std::is_same_v) { + if (val.is_type(type_TypedLink)) { + return util::serializer::print_value(val.template get(), state.group); + } + else { + return util::serializer::print_value(val); + } + } else { return util::serializer::print_value(val.template get()); } @@ -1241,13 +1268,13 @@ class Value : public ValueBase, public Subexpr2 { if (i != 0) { desc += ", "; } - desc += value_to_string(i); + desc += value_to_string(i, state); } desc += "}"; return desc; } else if (sz == 1) { - return value_to_string(0); + return value_to_string(0, state); } return ""; } @@ -2589,6 +2616,11 @@ class Columns : public Subexpr2 { return type_Link; } + ConstTableRef get_target_table() const override + { + return link_map().get_target_table(); + } + bool has_multiple_values() const override { return m_is_list || !m_link_map.only_unary_links(); @@ -2736,6 +2768,10 @@ class ColumnsCollection : public Subexpr2, public ColumnListBase { { return m_link_map.get_base_table(); } + ConstTableRef get_target_table() const override + { + return m_link_map.get_target_table()->get_opposite_table(m_column_key); + } Allocator& get_alloc() const { @@ -3786,7 +3822,7 @@ class SubColumnAggregate : public Subexpr2 { virtual std::string description(util::serializer::SerialisationState& state) const override { - util::serializer::SerialisationState empty_state(state.class_prefix); + util::serializer::SerialisationState empty_state(state.class_prefix, state.group); return state.describe_columns(m_link_map, ColKey()) + util::serializer::value_separator + Operation::description() + util::serializer::value_separator + m_column.description(empty_state); } @@ -4201,8 +4237,8 @@ class Compare : public Expression { m_left->description(state)); } else { - return util::serializer::print_value(m_left->description(state) + " " + TCond::description() + " " + - m_right->description(state)); + state.target_table = m_left->get_target_table(); + return m_left->description(state) + " " + TCond::description() + " " + m_right->description(state); } } diff --git a/src/realm/table.cpp b/src/realm/table.cpp index 288949c97a5..1742e6169bc 100644 --- a/src/realm/table.cpp +++ b/src/realm/table.cpp @@ -3182,7 +3182,6 @@ Obj Table::get_object_with_primary_key(Mixed primary_key) const DataType type = DataType(primary_key_col.get_type()); REALM_ASSERT((primary_key.is_null() && primary_key_col.get_attrs().test(col_attr_Nullable)) || primary_key.get_type() == type); - return m_clusters.get(m_index_accessors[primary_key_col.get_index().val]->find_first(primary_key)); } diff --git a/src/realm/util/serializer.cpp b/src/realm/util/serializer.cpp index 4c849d83967..385ec167cae 100644 --- a/src/realm/util/serializer.cpp +++ b/src/realm/util/serializer.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -164,16 +165,24 @@ std::string print_value<>(realm::ObjKey k) return ss.str(); } -template <> -std::string print_value<>(realm::ObjLink link) +std::string print_value(realm::ObjLink link, Group* g) { - std::stringstream ss; if (!link) { - ss << "NULL"; + return "NULL"; } else { - ss << "L" << link.get_table_key().value << ":" << link.get_obj_key().value; + TableRef target_table = g ? g->get_table(link.get_table_key()) : TableRef(); + if (ColKey pk_col = target_table ? target_table->get_primary_key_column() : ColKey{}) { + if (auto obj = target_table->try_get_object(link.get_obj_key())) { + auto pk_val = obj.get_any(pk_col); + std::ostringstream ostr; + ostr << "obj(" << util::serializer::print_value(target_table->get_name()) << "," << pk_val << ')'; + return ostr.str(); + } + } } + std::stringstream ss; + ss << "L" << link.get_table_key().value << ":" << link.get_obj_key().value; return ss.str(); } diff --git a/src/realm/util/serializer.hpp b/src/realm/util/serializer.hpp index cd18654750f..441a41e0bc8 100644 --- a/src/realm/util/serializer.hpp +++ b/src/realm/util/serializer.hpp @@ -39,6 +39,7 @@ class Timestamp; class LinkMap; class UUID; class TypeOfValue; +class Group; enum class ExpressionComparisonType : unsigned char; namespace util { @@ -68,8 +69,9 @@ template <> std::string print_value<>(realm::ObjectId); template <> std::string print_value<>(realm::ObjKey); -template <> -std::string print_value<>(realm::ObjLink); + +std::string print_value(realm::ObjLink, Group*); + template <> std::string print_value<>(realm::UUID); template <> @@ -97,8 +99,9 @@ std::string print_value(Optional value) StringData get_printable_table_name(StringData name, const std::string& prefix); struct SerialisationState { - SerialisationState(const std::string& prefix) + SerialisationState(const std::string& prefix, Group* g) : class_prefix(prefix) + , group(g) { } std::string describe_column(ConstTableRef table, ColKey col_key); @@ -109,6 +112,8 @@ struct SerialisationState { std::string get_variable_name(ConstTableRef table); std::vector subquery_prefix_list; std::string class_prefix; + Group* group; + ConstTableRef target_table; }; } // namespace serializer diff --git a/test/test_metrics.cpp b/test/test_metrics.cpp index 9eee090bb07..95dd3ab13a9 100644 --- a/test/test_metrics.cpp +++ b/test/test_metrics.cpp @@ -613,7 +613,7 @@ TEST(Metrics_LinkListQueries) CHECK_EQUAL(find_count(count_link_description, "=="), 1); std::string links_description = queries->at(3).get_description(); - CHECK_EQUAL(find_count(links_description, "O0"), 1); + CHECK_EQUAL(find_count(links_description, "L0:0"), 1); CHECK_EQUAL(find_count(links_description, column_names[ll_col_key]), 1); CHECK_EQUAL(find_count(links_description, "=="), 1); diff --git a/test/test_parser.cpp b/test/test_parser.cpp index 0c149a8b922..6732289ccbf 100644 --- a/test/test_parser.cpp +++ b/test/test_parser.cpp @@ -737,18 +737,17 @@ TEST(Parser_LinksToDifferentTable) obj.set(discount_active_col, discount_info[i].second); } - TableRef items = g.add_table("class_Items"); - ColKey item_name_col = items->add_column(type_String, "name"); + TableRef items = g.add_table_with_primary_key("class_Items", type_String, "name"); ColKey item_price_col = items->add_column(type_Double, "price"); ColKey item_discount_col = items->add_column(*discounts, "discount"); using item_t = std::pair; - std::vector item_info = {{"milk", 5.5}, {"oranges", 4.0}, {"pizza", 9.5}, {"cereal", 6.5}}; + std::vector item_info = { + {"milk", 5.5}, {"oranges", 4.0}, {"pizza", 9.5}, {"cereal", 6.5}, {"coffee", 17.5}}; std::vector item_keys; - items->create_objects(item_info.size(), item_keys); - for (size_t i = 0; i < item_keys.size(); ++i) { - Obj obj = items->get_object(item_keys[i]); - obj.set(item_name_col, StringData(item_info[i].first)); - obj.set(item_price_col, item_info[i].second); + for (auto& item : item_info) { + Obj obj = items->create_object_with_primary_key(item.first); + obj.set(item_price_col, item.second); + item_keys.push_back(obj.get_key()); } items->get_object(item_keys[0]).set(item_discount_col, discount_keys[2]); // milk -0.50 items->get_object(item_keys[2]).set(item_discount_col, discount_keys[1]); // pizza -2.5 @@ -781,6 +780,9 @@ TEST(Parser_LinksToDifferentTable) list_2.add(item_keys[2]); list_2.add(item_keys[3]); + verify_query(test_context, t, "items = obj('Items', 'coffee')", 0); // nobody buys coffee + verify_query(test_context, t, "items = obj('Items', 'milk')", 2); // but milk + verify_query(test_context, t, "items = O0", 2); // how many people bought milk? verify_query(test_context, t, "items.@count > 2", 3); // how many people bought more than two items? verify_query(test_context, t, "items.price > 3.0", 3); // how many people buy items over $3.0? verify_query(test_context, t, "items.name ==[c] 'milk'", 2); // how many people buy milk? @@ -3362,7 +3364,7 @@ TEST(Parser_BacklinksIndex) TEST(Parser_SubqueryVariableNames) { Group g; - util::serializer::SerialisationState test_state(""); + util::serializer::SerialisationState test_state("", nullptr); TableRef test_table = g.add_table("test"); @@ -4102,7 +4104,7 @@ TEST(Parser_Object) Query q0 = table->where().and_query(table->column(link_col) == tv.get_object(0)); std::string description = q0.get_description(); // shouldn't throw - CHECK(description.find("O0") != std::string::npos); + CHECK(description.find("L0:0") != std::string::npos); Query q1 = table->column(link_col) == realm::null(); description = q1.get_description(); // shouldn't throw @@ -4994,6 +4996,8 @@ TEST(Parser_DictionaryObjects) CHECK_EQUAL(q.count(), 1); verify_query(test_context, persons, "pets.@values.age > 4", 1); + verify_query(test_context, persons, "pets.@values == obj('dog', 'pluto')", 1); + verify_query(test_context, persons, "pets.@values == ANY { obj('dog', 'pluto'), obj('dog', 'astro') }", 2); } TEST_TYPES(Parser_DictionaryAggregates, Prop, Prop, Prop) @@ -5511,4 +5515,89 @@ TEST(Parser_Between) CHECK_THROW_ANY(verify_query(test_context, table, "NONE scores between {10, 12}", 1)); } +TEST(Parser_PrimaryKey) +{ + UUID u1("3b241101-e2bb-4255-8caf-4136c566a961"); + ObjectId o1 = ObjectId::gen(); + Group g; + auto table_int = g.add_table_with_primary_key("class_Int", type_Int, "_id"); + auto table_string = g.add_table_with_primary_key("class_String", type_String, "_id"); + auto table_oid = g.add_table_with_primary_key("class_Oid", type_ObjectId, "_id"); + auto table_uuid = g.add_table_with_primary_key("class_Uuid", type_UUID, "_id"); + + auto origin = g.add_table("origin"); + auto col_int = origin->add_column(*table_int, "int"); + auto col_string = origin->add_column(*table_string, "string"); + auto col_oid = origin->add_column(*table_oid, "oid"); + auto col_uuid = origin->add_column(*table_uuid, "uuid"); + auto col_any = origin->add_column(type_Mixed, "mixed"); + + + auto linking = g.add_table("linking"); + auto col_link = linking->add_column(*origin, "link"); + + auto target = table_int->create_object_with_primary_key(1); + origin->create_object().set(col_int, target.get_key()).set(col_any, Mixed(target.get_link())); + target = table_string->create_object_with_primary_key("first"); + origin->create_object().set(col_string, target.get_key()).set(col_any, Mixed(target.get_link())); + target = table_oid->create_object_with_primary_key(o1); + origin->create_object().set(col_oid, target.get_key()).set(col_any, Mixed(target.get_link())); + target = table_uuid->create_object_with_primary_key(u1); + origin->create_object().set(col_uuid, target.get_key()).set(col_any, Mixed(target.get_link())); + + for (auto o : *origin) { + linking->create_object().set(col_link, o.get_key()); + } + + std::string query_string = "int == obj(\"class_Int\",1)"; + Query q = origin->query(query_string); + CHECK_EQUAL(q.count(), 1); + CHECK_EQUAL(q.get_description(), query_string); + + query_string = "mixed == obj(\"class_Int\",1)"; + q = origin->query(query_string); + CHECK_EQUAL(q.count(), 1); + CHECK_EQUAL(q.get_description(), query_string); + + query_string = "string == obj(\"class_String\",\"first\")"; + q = origin->query(query_string); + CHECK_EQUAL(q.count(), 1); + CHECK_EQUAL(q.get_description(), query_string); + + query_string = "oid == obj(\"class_Oid\"," + util::serializer::print_value(o1) + ")"; + q = origin->query(query_string); + CHECK_EQUAL(q.count(), 1); + CHECK_EQUAL(q.get_description(), query_string); + + query_string = "uuid == obj(\"class_Uuid\"," + util::serializer::print_value(u1) + ")"; + q = origin->query(query_string); + CHECK_EQUAL(q.count(), 1); + CHECK_EQUAL(q.get_description(), query_string); + + query_string = "link.int == obj(\"class_Int\",1)"; + q = linking->query(query_string); + CHECK_EQUAL(q.count(), 1); + CHECK_EQUAL(q.get_description(), query_string); + + query_string = "link.mixed == obj(\"class_Int\",1)"; + q = linking->query(query_string); + CHECK_EQUAL(q.count(), 1); + CHECK_EQUAL(q.get_description(), query_string); + + query_string = "link.string == obj(\"class_String\",\"first\")"; + q = linking->query(query_string); + CHECK_EQUAL(q.count(), 1); + CHECK_EQUAL(q.get_description(), query_string); + + query_string = "link.oid == obj(\"class_Oid\"," + util::serializer::print_value(o1) + ")"; + q = linking->query(query_string); + CHECK_EQUAL(q.count(), 1); + CHECK_EQUAL(q.get_description(), query_string); + + query_string = "link.uuid == obj(\"class_Uuid\"," + util::serializer::print_value(u1) + ")"; + q = linking->query(query_string); + CHECK_EQUAL(q.count(), 1); + CHECK_EQUAL(q.get_description(), query_string); +} + #endif // TEST_PARSER From 6b043981ac92537ce7ec6e29d1d47374e8a31208 Mon Sep 17 00:00:00 2001 From: Kenneth Geisshirt Date: Thu, 15 Sep 2022 17:14:23 +0200 Subject: [PATCH 10/14] Fix handling of 4-byte UTF8 values on Windows (#5803) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Improve case_map function on non-Windows platforms Co-authored-by: Jørgen Edelbo --- CHANGELOG.md | 1 + Package.swift | 1 + src/realm/exceptions.hpp | 19 +++ src/realm/parser/query_parser.hpp | 16 --- src/realm/query.cpp | 14 --- src/realm/query.hpp | 2 - src/realm/query_engine.hpp | 58 +-------- src/realm/unicode.cpp | 196 ++++++++++++++---------------- test/test_index_string.cpp | 22 ++++ test/test_parser.cpp | 33 ++--- test/test_query2.cpp | 48 -------- 11 files changed, 157 insertions(+), 253 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bb79c96d3b..0e32bc3858d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ * Fixed `realm_query_parse_for_list` ignoring existing query ([#5850](https://github.com/realm/realm-core/pull/5850)). * Fixed not allowing asymmetric tables in partition based sync ([#5691](https://github.com/realm/realm-core/issues/5691)). * Disable auto refresh for old realm instance passed to migration callbacks. ([#5856](https://github.com/realm/realm-core/pull/5856)). +* If a case insensitive query searched for a string including an 4-byte UTF8 character, the program would crash ([#5825](https://github.com/realm/realm-core/issues/5825), since v2.3.0) ### Breaking changes * None. diff --git a/Package.swift b/Package.swift index 1f11542b1ae..1aef10edadf 100644 --- a/Package.swift +++ b/Package.swift @@ -120,6 +120,7 @@ let notSyncServerSources: [String] = [ "realm/table_cluster_tree.cpp", "realm/table_ref.cpp", "realm/table_view.cpp", + "realm/transaction.cpp", "realm/unicode.cpp", "realm/util", "realm/utilities.cpp", diff --git a/src/realm/exceptions.hpp b/src/realm/exceptions.hpp index 549023aec65..a560cb405a8 100644 --- a/src/realm/exceptions.hpp +++ b/src/realm/exceptions.hpp @@ -176,6 +176,25 @@ class NoSubscriptionForWrite : public std::runtime_error { NoSubscriptionForWrite(const std::string& msg); }; +namespace query_parser { + +/// Exception thrown when parsing fails due to invalid syntax. +struct SyntaxError : std::runtime_error { + using std::runtime_error::runtime_error; +}; + +/// Exception thrown when binding a syntactically valid query string in a +/// context where it does not make sense. +struct InvalidQueryError : std::runtime_error { + using std::runtime_error::runtime_error; +}; + +/// Exception thrown when there is a problem accessing the arguments in a query string +struct InvalidQueryArgError : std::invalid_argument { + using std::invalid_argument::invalid_argument; +}; + +} // namespace query_parser /// The \c LogicError exception class is intended to be thrown only when /// applications (or bindings) violate rules that are stated (or ought to have diff --git a/src/realm/parser/query_parser.hpp b/src/realm/parser/query_parser.hpp index 19999887a8c..f4338a66b62 100644 --- a/src/realm/parser/query_parser.hpp +++ b/src/realm/parser/query_parser.hpp @@ -31,22 +31,6 @@ namespace realm::query_parser { -/// Exception thrown when parsing fails due to invalid syntax. -struct SyntaxError : std::runtime_error { - using std::runtime_error::runtime_error; -}; - -/// Exception thrown when binding a syntactically valid query string in a -/// context where it does not make sense. -struct InvalidQueryError : std::runtime_error { - using std::runtime_error::runtime_error; -}; - -/// Exception thrown when there is a problem accessing the arguments in a query string -struct InvalidQueryArgError : std::invalid_argument { - using std::invalid_argument::invalid_argument; -}; - struct AnyContext { template T unbox(const std::any& wrapper) diff --git a/src/realm/query.cpp b/src/realm/query.cpp index cc038c638cd..f810d800079 100644 --- a/src/realm/query.cpp +++ b/src/realm/query.cpp @@ -1827,20 +1827,6 @@ void* Query::query_thread(void* arg) #endif // REALM_MULTITHREADQUERY -std::string Query::validate() const -{ - if (!m_groups.size()) - return ""; - - if (error_code != "") // errors detected by QueryInterface - return error_code; - - if (!root_node()) - return "Syntax error"; - - return root_node()->validate(); // errors detected by QueryEngine -} - std::string Query::get_description(util::serializer::SerialisationState& state) const { std::string description; diff --git a/src/realm/query.hpp b/src/realm/query.hpp index 85b8d9368ee..fc45606f8ce 100644 --- a/src/realm/query.hpp +++ b/src/realm/query.hpp @@ -320,8 +320,6 @@ class Query final { // or empty vector if the query is not associated with a table. TableVersions sync_view_if_needed() const; - std::string validate() const; - std::string get_description(const std::string& class_prefix = "") const; std::string get_description(util::serializer::SerialisationState& state) const; diff --git a/src/realm/query_engine.hpp b/src/realm/query_engine.hpp index 8ca5f42d65c..3159dbe69f1 100644 --- a/src/realm/query_engine.hpp +++ b/src/realm/query_engine.hpp @@ -225,16 +225,6 @@ class ParentNode { ArrayPayload* source_column); - virtual std::string validate() - { - if (error_code != "") - return error_code; - if (m_child == nullptr) - return ""; - else - return m_child->validate(); - } - ParentNode(const ParentNode& from); void add_child(std::unique_ptr child) @@ -320,7 +310,6 @@ class ParentNode { ConstTableRef m_table = ConstTableRef(); const Cluster* m_cluster = nullptr; QueryStateBase* m_state = nullptr; - std::string error_code; static std::vector s_dummy_keys; ColumnType get_real_column_type(ColKey key) @@ -1587,7 +1576,7 @@ class StringNode : public StringNodeBase { auto upper = case_map(v, true); auto lower = case_map(v, false); if (!upper || !lower) { - error_code = "Malformed UTF-8: " + std::string(v); + throw std::runtime_error(util::format("Malformed UTF-8: %1", v)); } else { m_ucase = std::move(*upper); @@ -1712,7 +1701,7 @@ class StringNode : public StringNodeBase { auto upper = case_map(v, true); auto lower = case_map(v, false); if (!upper || !lower) { - error_code = "Malformed UTF-8: " + std::string(v); + throw query_parser::InvalidQueryError(util::format("Malformed UTF-8: %1", v)); } else { m_ucase = std::move(*upper); @@ -1926,7 +1915,7 @@ class StringNode : public StringNodeEqualBase { auto upper = case_map(v, true); auto lower = case_map(v, false); if (!upper || !lower) { - error_code = "Malformed UTF-8: " + std::string(v); + throw query_parser::InvalidQueryError(util::format("Malformed UTF-8: %1", v)); } else { m_ucase = std::move(*upper); @@ -2111,27 +2100,6 @@ class OrNode : public ParentNode { return index; } - std::string validate() override - { - if (error_code != "") - return error_code; - if (m_conditions.size() == 0) - return "Missing left-hand side of OR"; - if (m_conditions.size() == 1) - return "Missing right-hand side of OR"; - std::string s; - if (m_child != 0) - s = m_child->validate(); - if (s != "") - return s; - for (size_t i = 0; i < m_conditions.size(); ++i) { - s = m_conditions[i]->validate(); - if (s != "") - return s; - } - return ""; - } - std::unique_ptr clone() const override { return std::unique_ptr(new OrNode(*this)); @@ -2171,6 +2139,9 @@ class NotNode : public ParentNode { : m_condition(std::move(condition)) { m_dT = 50.0; + if (!m_condition) { + throw query_parser::InvalidQueryError("Missing argument to Not"); + } } void table_changed() override @@ -2199,23 +2170,6 @@ class NotNode : public ParentNode { size_t find_first_local(size_t start, size_t end) override; - std::string validate() override - { - if (error_code != "") - return error_code; - if (m_condition == 0) - return "Missing argument to Not"; - std::string s; - if (m_child != 0) - s = m_child->validate(); - if (s != "") - return s; - s = m_condition->validate(); - if (s != "") - return s; - return ""; - } - std::string describe(util::serializer::SerialisationState& state) const override { if (m_condition) { diff --git a/src/realm/unicode.cpp b/src/realm/unicode.cpp index cac213f821d..d7553619dca 100644 --- a/src/realm/unicode.cpp +++ b/src/realm/unicode.cpp @@ -274,79 +274,6 @@ bool utf8_compare(StringData string1, StringData string2) return false; } -// Here is a version for Windows that may be closer to what is ultimately needed. -/* -bool case_map(const char* begin, const char* end, StringBuffer& dest, bool upper) -{ -const int wide_buffer_size = 32; -wchar_t wide_buffer[wide_buffer_size]; - -dest.resize(end-begin); -size_t dest_offset = 0; - -for (;;) { -int num_out; - -// Decode -{ -size_t num_in = end - begin; -if (size_t(32) <= num_in) { -num_out = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, begin, 32, wide_buffer, wide_buffer_size); -if (num_out != 0) { -begin += 32; -goto convert; -} -if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return false; -} -if (num_in == 0) break; -int n = num_in < size_t(8) ? int(num_in) : 8; -num_out = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, begin, n, wide_buffer, wide_buffer_size); -if (num_out != 0) { -begin += n; -goto convert; -} -return false; -} - -convert: -if (upper) { -for (int i=0; i::max(), free)) free = std::numeric_limits::max(); -int n = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, wide_buffer, num_out, -dest.data() + dest_offset, int(free), 0, 0); -if (i != 0) { -dest_offset += n; -continue; -} -if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return false; -size_t dest_size = dest.size(); -if (int_multiply_with_overflow_detect(dest_size, 2)) { -if (dest_size == std::numeric_limits::max()) return false; -dest_size = std::numeric_limits::max(); -} -dest.resize(dest_size); -goto encode; -} -} - -dest.resize(dest_offset); -return true; -} -*/ - - // Converts UTF-8 source into upper or lower case. This function // preserves the byte length of each UTF-8 character in following way: // If an output character differs in size, it is simply substituded by @@ -358,29 +285,42 @@ util::Optional case_map(StringData source, bool upper) result.resize(source.size()); #if defined(_WIN32) + constexpr int tmp_buffer_size = 32; const char* begin = source.data(); const char* end = begin + source.size(); auto output = result.begin(); while (begin != end) { - int n = static_cast(sequence_length(*begin)); - if (n == 0 || end - begin < n) - return util::none; + auto n = end - begin; + if (n > tmp_buffer_size) { + // Break the input string into chunks - but don't break in the middle of a multibyte character + const char* p = begin; + const char* buffer_end = begin + tmp_buffer_size; + while (p < buffer_end) { + size_t len = sequence_length(*p); + p += len; + if (p > buffer_end) { + p -= len; + break; + } + } + n = p - begin; + } - wchar_t tmp[2]; // FIXME: Why no room for UTF-16 surrogate + wchar_t tmp[tmp_buffer_size]; - int n2 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, begin, n, tmp, 1); + int n2 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, begin, int(n), tmp, tmp_buffer_size); if (n2 == 0) return util::none; - REALM_ASSERT(n2 == 1); - tmp[n2] = 0; + if (n2 < tmp_buffer_size) + tmp[n2] = 0; // Note: If tmp[0] == 0, it is because the string contains a // null-chacarcter, which is perfectly fine. - wchar_t mapped_tmp[2]; - LCMapStringEx(LOCALE_NAME_INVARIANT, upper ? LCMAP_UPPERCASE : LCMAP_LOWERCASE, tmp, 1, mapped_tmp, 2, - nullptr, nullptr, 0); + wchar_t mapped_tmp[tmp_buffer_size]; + LCMapStringEx(LOCALE_NAME_INVARIANT, upper ? LCMAP_UPPERCASE : LCMAP_LOWERCASE, tmp, n2, mapped_tmp, + tmp_buffer_size, nullptr, nullptr, 0); // FIXME: The intention is to use flag 'WC_ERR_INVALID_CHARS' // to catch invalid UTF-8. Even though the documentation says @@ -388,7 +328,8 @@ util::Optional case_map(StringData source, bool upper) // the flag is specified, the function fails with error // ERROR_INVALID_FLAGS. DWORD flags = 0; - int n3 = WideCharToMultiByte(CP_UTF8, flags, mapped_tmp, 1, &*output, static_cast(end - begin), 0, 0); + auto m = static_cast(end - begin); + int n3 = WideCharToMultiByte(CP_UTF8, flags, mapped_tmp, n2, &*output, m, 0, 0); if (n3 == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) return util::none; @@ -402,32 +343,75 @@ util::Optional case_map(StringData source, bool upper) return result; #else - // FIXME: Implement this! Note that this is trivial in C++11 due - // to its built-in support for UTF-8. In C++03 it is trivial when - // __STDC_ISO_10646__ is defined. Also consider using ICU. Maybe - // GNU has something to offer too. - - // For now we handle just the ASCII subset + size_t sz = source.size(); typedef std::char_traits traits; - if (upper) { - size_t n = source.size(); - for (size_t i = 0; i < n; ++i) { - char c = source[i]; - if (traits::lt(0x60, c) && traits::lt(c, 0x7B)) - c = traits::to_char_type(traits::to_int_type(c) - 0x20); - result[i] = c; + for (size_t i = 0; i < sz; ++i) { + char c = source[i]; + auto int_val = traits::to_int_type(c); + + auto copy_bytes = [&](size_t n) { + if (i + n > sz) { + return false; + } + for (size_t j = 1; j < n; j++) { + result[i++] = c; + c = source[i]; + if ((c & 0xC0) != 0x80) { + return false; + } + } + return true; + }; + + if (int_val < 0x80) { + // Handle ASCII + if (upper && (c >= 'a' && c <= 'z')) { + c -= 0x20; + } + else if (!upper && (c >= 'A' && c <= 'Z')) { + c += 0x20; + } } - } - else { // lower - size_t n = source.size(); - for (size_t i = 0; i < n; ++i) { - char c = source[i]; - if (traits::lt(0x40, c) && traits::lt(c, 0x5B)) - c = traits::to_char_type(traits::to_int_type(c) + 0x20); - result[i] = c; + else { + if ((int_val & 0xE0) == 0xc0) { + // 2 byte utf-8 + if (i + 2 > sz) { + return {}; + } + c = source[i + 1]; + if ((c & 0xC0) != 0x80) { + return {}; + } + auto u = ((int_val << 6) + (traits::to_int_type(c) & 0x3F)) & 0x7FF; + // Handle some Latin-1 supplement characters + if (upper && (u >= 0xE0 && u <= 0xFE && u != 0xF7)) { + u -= 0x20; + } + else if (!upper && (u >= 0xC0 && u <= 0xDE && u != 0xD7)) { + u += 0x20; + } + + result[i++] = static_cast((u >> 6) | 0xC0); + c = static_cast((u & 0x3f) | 0x80); + } + else if ((int_val & 0xF0) == 0xE0) { + // 3 byte utf-8 + if (!copy_bytes(3)) { + return {}; + } + } + else if ((int_val & 0xF8) == 0xF0) { + // 4 byte utf-8 + if (!copy_bytes(4)) { + return {}; + } + } + else { + return {}; + } } + result[i] = c; } - return result; #endif } diff --git a/test/test_index_string.cpp b/test/test_index_string.cpp index db03d519f4a..5f5dee20914 100644 --- a/test/test_index_string.cpp +++ b/test/test_index_string.cpp @@ -1826,4 +1826,26 @@ TEST(StringIndex_MixedEqualBitPattern) CHECK_EQUAL(tv.get_object(1).get_any(col), val1); } +TEST(Unicode_Casemap) +{ + std::string inp = "A very old house 🏠 is on 🔥, we have to save the 🦄"; + auto out = case_map(inp, true); + if (CHECK(out)) { + CHECK_EQUAL(*out, "A VERY OLD HOUSE 🏠 IS ON 🔥, WE HAVE TO SAVE THE 🦄"); + } + + StringData trailing_garbage(inp.data(), 19); // String terminated inside icon + out = case_map(trailing_garbage, true); + CHECK_NOT(out); + + inp = "rødgrød med fløde"; + out = case_map(inp, true); + if (CHECK(out)) { + CHECK_EQUAL(*out, "RØDGRØD MED FLØDE"); + } + out = case_map(out, false); + if (CHECK(out)) { + CHECK_EQUAL(*out, inp); + } +} #endif // TEST_INDEX_STRING diff --git a/test/test_parser.cpp b/test/test_parser.cpp index 6732289ccbf..21529a22fa6 100644 --- a/test/test_parser.cpp +++ b/test/test_parser.cpp @@ -826,7 +826,7 @@ TEST(Parser_StringOperations) TableRef t = g.add_table("person"); ColKey name_col = t->add_column(type_String, "name", true); ColKey link_col = t->add_column(*t, "father"); - std::vector names = {"Billy", "Bob", "Joe", "Jake", "Joel"}; + std::vector names = {"Billy", "Bob", "Joe", "Jake", "Joel", "Unicorn🦄"}; std::vector people_keys; t->create_objects(names.size(), people_keys); for (size_t i = 0; i < t->size(); ++i) { @@ -836,16 +836,17 @@ TEST(Parser_StringOperations) } t->create_object(); // null t->get_object(people_keys[4]).set_null(link_col); + size_t nb_names = names.size(); verify_query(test_context, t, "name == 'Bob'", 1); verify_query(test_context, t, "father.name == 'Bob'", 1); verify_query(test_context, t, "name ==[c] 'Bob'", 1); verify_query(test_context, t, "father.name ==[c] 'Bob'", 1); - verify_query(test_context, t, "name != 'Bob'", 5); - verify_query(test_context, t, "father.name != 'Bob'", 5); - verify_query(test_context, t, "name !=[c] 'bOB'", 5); - verify_query(test_context, t, "father.name !=[c] 'bOB'", 5); + verify_query(test_context, t, "name != 'Bob'", nb_names); + verify_query(test_context, t, "father.name != 'Bob'", nb_names); + verify_query(test_context, t, "name !=[c] 'bOB'", nb_names); + verify_query(test_context, t, "father.name !=[c] 'bOB'", nb_names); verify_query(test_context, t, "name contains \"oe\"", 2); verify_query(test_context, t, "father.name contains \"oe\"", 2); @@ -867,23 +868,25 @@ TEST(Parser_StringOperations) verify_query(test_context, t, "name like[c] \"?O?\"", 2); verify_query(test_context, t, "father.name like[c] \"?O?\"", 2); + verify_query(test_context, t, "name ==[c] 'unicorn🦄'", 1); + verify_query(test_context, t, "name == NULL", 1); verify_query(test_context, t, "name == nil", 1); verify_query(test_context, t, "NULL == name", 1); - verify_query(test_context, t, "name != NULL", 5); - verify_query(test_context, t, "NULL != name", 5); + verify_query(test_context, t, "name != NULL", nb_names); + verify_query(test_context, t, "NULL != name", nb_names); verify_query(test_context, t, "name ==[c] NULL", 1); verify_query(test_context, t, "NULL ==[c] name", 1); - verify_query(test_context, t, "name !=[c] NULL", 5); - verify_query(test_context, t, "NULL !=[c] name", 5); + verify_query(test_context, t, "name !=[c] NULL", nb_names); + verify_query(test_context, t, "NULL !=[c] name", nb_names); // for strings 'NULL' is also a synonym for the null string - verify_query(test_context, t, "name CONTAINS NULL", 6); - verify_query(test_context, t, "name CONTAINS[c] NULL", 6); - verify_query(test_context, t, "name BEGINSWITH NULL", 6); - verify_query(test_context, t, "name BEGINSWITH[c] NULL", 6); - verify_query(test_context, t, "name ENDSWITH NULL", 6); - verify_query(test_context, t, "name ENDSWITH[c] NULL", 6); + verify_query(test_context, t, "name CONTAINS NULL", t->size()); + verify_query(test_context, t, "name CONTAINS[c] NULL", t->size()); + verify_query(test_context, t, "name BEGINSWITH NULL", t->size()); + verify_query(test_context, t, "name BEGINSWITH[c] NULL", t->size()); + verify_query(test_context, t, "name ENDSWITH NULL", t->size()); + verify_query(test_context, t, "name ENDSWITH[c] NULL", t->size()); verify_query(test_context, t, "name LIKE NULL", 1); verify_query(test_context, t, "name LIKE[c] NULL", 1); diff --git a/test/test_query2.cpp b/test/test_query2.cpp index 50fe64b2439..d9bd7696d20 100644 --- a/test/test_query2.cpp +++ b/test/test_query2.cpp @@ -864,54 +864,6 @@ TEST(Query_FindAllContainsUnicode) CHECK_EQUAL(3, tv2[3].get(col_id)); } -TEST(Query_SyntaxCheck) -{ - Table table; - auto col_int = table.add_column(type_Int, "1"); - table.add_column(type_String, "2"); - - std::string s; - - table.create_object().set_all(1, "a"); - table.create_object().set_all(2, "a"); - table.create_object().set_all(3, "X"); - - Query q1 = table.where().equal(col_int, 2).end_group(); - s = q1.validate(); - CHECK(s != ""); - - Query q2 = table.where().group().group().equal(col_int, 2).end_group(); - s = q2.validate(); - CHECK(s != ""); - - Query q3 = table.where().equal(col_int, 2).Or(); - s = q3.validate(); - CHECK(s != ""); - - Query q4 = table.where().Or().equal(col_int, 2); - s = q4.validate(); - CHECK(s != ""); - - Query q5 = table.where().equal(col_int, 2); - s = q5.validate(); - CHECK(s == ""); - - Query q6 = table.where().group().equal(col_int, 2); - s = q6.validate(); - CHECK(s != ""); - - // FIXME: Work is currently underway to fully support locale - // independent case folding as defined by Unicode. Reenable this test - // when is becomes available. - /* - Query q7 = ttt.where().equal(1, "\xa0", false); -#ifdef REALM_DEBUG - s = q7.verify(); - CHECK(s != ""); -#endif - */ -} - TEST(Query_TestTV_where) { // When using .where(&tv), tv can have any order, and the resulting view will retain its order From c4b790a5ba2eee3530a1fe2f5ecb597e58e2ac3e Mon Sep 17 00:00:00 2001 From: James Stone Date: Thu, 15 Sep 2022 10:16:59 -0700 Subject: [PATCH 11/14] Traversal functions use IteratorControl values rather than true/false which is more expressive (#5857) --- CHANGELOG.md | 2 +- src/realm/bplustree.cpp | 4 +-- src/realm/bplustree.hpp | 18 +++++------ src/realm/cluster.cpp | 31 +++++++++---------- src/realm/cluster_tree.cpp | 6 ++-- src/realm/cluster_tree.hpp | 6 ++-- src/realm/collection.cpp | 6 ++-- src/realm/dictionary.cpp | 8 ++--- src/realm/dictionary.hpp | 6 ++-- src/realm/obj.cpp | 18 +++++------ src/realm/obj_list.hpp | 2 +- .../object-store/impl/deep_change_checker.cpp | 2 +- src/realm/query.cpp | 18 +++++------ src/realm/query_expression.cpp | 9 ++---- .../sync/noinst/pending_bootstrap_store.cpp | 2 +- src/realm/table.cpp | 30 +++++++++--------- src/realm/table.hpp | 8 ++--- src/realm/table_cluster_tree.cpp | 15 ++++----- src/realm/table_tpl.hpp | 3 +- src/realm/table_view.cpp | 2 +- src/realm/utilities.hpp | 2 ++ test/test_table.cpp | 2 +- 22 files changed, 95 insertions(+), 105 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e32bc3858d..01109a084f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,7 @@ ----------- ### Internals -* None. +* Traversal functions use a typed IteratorControl value rather than true/false. ([#5857](https://github.com/realm/realm-core/issues/5857)) ---------------------------------------------- diff --git a/src/realm/bplustree.cpp b/src/realm/bplustree.cpp index 3d536eb590a..039b643b617 100644 --- a/src/realm/bplustree.cpp +++ b/src/realm/bplustree.cpp @@ -218,7 +218,7 @@ size_t BPlusTreeLeaf::bptree_erase(size_t ndx, EraseFunc func) bool BPlusTreeLeaf::bptree_traverse(TraverseFunc func) { - return func(this, 0); + return func(this, 0) == IteratorControl::Stop; } /****************************** BPlusTreeInner *******************************/ @@ -465,7 +465,7 @@ bool BPlusTreeInner::bptree_traverse(TraverseFunc func) bool child_is_leaf = !Array::get_is_inner_bptree_node_from_header(child_header); if (child_is_leaf) { auto leaf = cache_leaf(mem, i, child_offset + m_my_offset); - done = func(leaf, child_offset + m_my_offset); + done = (func(leaf, child_offset + m_my_offset) == IteratorControl::Stop); } else { BPlusTreeInner node(m_tree); diff --git a/src/realm/bplustree.hpp b/src/realm/bplustree.hpp index 2fd6e24ff99..5980916b461 100644 --- a/src/realm/bplustree.hpp +++ b/src/realm/bplustree.hpp @@ -50,8 +50,8 @@ class BPlusTreeNode { // Erase element at erase_pos. May cause nodes to be merged using EraseFunc = util::FunctionRef; // Function to be called for all leaves in the tree until the function - // returns 'true'. 'offset' gives index of the first element in the leaf. - using TraverseFunc = util::FunctionRef; + // returns 'IteratorControl::Stop'. 'offset' gives index of the first element in the leaf. + using TraverseFunc = util::FunctionRef; BPlusTreeNode(BPlusTreeBase* tree) : m_tree(tree) @@ -392,7 +392,7 @@ class BPlusTree : public BPlusTreeBase { for (size_t i = 0; i < sz; i++) { all_values.push_back(leaf->get(i)); } - return false; + return IteratorControl::AdvanceToNext; }; m_root->bptree_traverse(func); @@ -497,9 +497,9 @@ class BPlusTree : public BPlusTreeBase { auto i = leaf->find_first(value, 0, sz); if (i < sz) { result = i + offset; - return true; + return IteratorControl::Stop; } - return false; + return IteratorControl::AdvanceToNext; }; m_root->bptree_traverse(func); @@ -516,7 +516,7 @@ class BPlusTree : public BPlusTreeBase { while ((i = leaf->find_first(value, i + 1, sz)) < sz) { callback(i + offset); } - return false; + return IteratorControl::AdvanceToNext; }; m_root->bptree_traverse(func); @@ -532,7 +532,7 @@ class BPlusTree : public BPlusTreeBase { for (size_t i = 0; i < sz; i++) { o << indent << leaf->get(i) << std::endl; } - return false; + return IteratorControl::AdvanceToNext; }; m_root->bptree_traverse(func); @@ -593,7 +593,7 @@ typename SumAggType::ResultType bptree_sum(const BPlusTree& tree, size_t* auto val = leaf->get(i); agg.accumulate(val); } - return false; + return IteratorControl::AdvanceToNext; }; tree.traverse(func); @@ -625,7 +625,7 @@ util::Optional::type> bptree_min_max(const BPlu *return_ndx = i + offset; } } - return false; + return IteratorControl::AdvanceToNext; }; tree.traverse(func); diff --git a/src/realm/cluster.cpp b/src/realm/cluster.cpp index c79d5148bdf..679201b9f9e 100644 --- a/src/realm/cluster.cpp +++ b/src/realm/cluster.cpp @@ -133,7 +133,7 @@ void Cluster::create() arr.create(); arr.set_parent(this, col_ndx.val + s_first_col_index); arr.update_parent(); - return false; + return IteratorControl::AdvanceToNext; } switch (type) { case col_type_Int: @@ -192,7 +192,7 @@ void Cluster::create() default: throw LogicError(LogicError::illegal_type); } - return false; + return IteratorControl::AdvanceToNext; }; m_tree_top.for_each_and_every_column(column_initialize); @@ -373,7 +373,7 @@ void Cluster::insert_row(size_t ndx, ObjKey k, const FieldValues& init_values) arr.set_parent(this, col_ndx.val + s_first_col_index); arr.init_from_parent(); arr.insert(ndx, 0); - return false; + return IteratorControl::AdvanceToNext; } bool nullable = attr.test(col_attr_Nullable); @@ -434,7 +434,7 @@ void Cluster::insert_row(size_t ndx, ObjKey k, const FieldValues& init_values) REALM_ASSERT(false); break; } - return false; + return IteratorControl::AdvanceToNext; }; m_tree_top.for_each_and_every_column(insert_in_column); } @@ -464,7 +464,7 @@ void Cluster::move(size_t ndx, ClusterNode* new_node, int64_t offset) if (attr.test(col_attr_Collection)) { do_move(ndx, col_key, new_leaf); - return false; + return IteratorControl::AdvanceToNext; } switch (type) { @@ -523,7 +523,7 @@ void Cluster::move(size_t ndx, ClusterNode* new_node, int64_t offset) REALM_ASSERT(false); break; } - return false; + return IteratorControl::AdvanceToNext; }; m_tree_top.for_each_and_every_column(move_from_column); for (size_t i = ndx; i < m_keys.size(); i++) { @@ -878,7 +878,7 @@ size_t Cluster::erase(ObjKey key, CascadeState& state) values.erase(ndx); - return false; + return IteratorControl::AdvanceToNext; } switch (col_type) { @@ -942,7 +942,7 @@ size_t Cluster::erase(ObjKey key, CascadeState& state) REALM_ASSERT(false); break; } - return false; + return IteratorControl::AdvanceToNext; }; m_tree_top.for_each_and_every_column(erase_in_column); @@ -1003,7 +1003,7 @@ void Cluster::nullify_incoming_links(ObjKey key, CascadeState& state) values.copy_on_write(); values.nullify_fwd_links(ndx, state); - return false; + return IteratorControl::AdvanceToNext; }; m_tree_top.get_owning_table()->for_each_backlink_column(nullify_fwd_links); @@ -1165,7 +1165,7 @@ void Cluster::verify() const // FIXME: Nullable primitives break; } - return false; + return IteratorControl::AdvanceToNext; } else if (attr.test(col_attr_Dictionary)) { ArrayRef arr(get_alloc()); @@ -1186,7 +1186,7 @@ void Cluster::verify() const cluster.verify(); } } - return false; + return IteratorControl::AdvanceToNext; } else if (attr.test(col_attr_Set)) { ArrayRef arr(get_alloc()); @@ -1242,7 +1242,7 @@ void Cluster::verify() const // FIXME: Nullable primitives break; } - return false; + return IteratorControl::AdvanceToNext; } switch (col_type) { @@ -1293,7 +1293,7 @@ void Cluster::verify() const default: break; } - return false; + return IteratorControl::AdvanceToNext; }; m_tree_top.for_each_and_every_column(verify_column); @@ -1339,7 +1339,7 @@ void Cluster::dump_objects(int64_t key_offset, std::string lead) const } } std::cout << "}"; - return false; + return IteratorControl::AdvanceToNext; } switch (col.get_type()) { @@ -1392,7 +1392,6 @@ void Cluster::dump_objects(int64_t key_offset, std::string lead) const else std::cout << ", null"; break; - break; } case col_type_String: { ArrayString arr(m_alloc); @@ -1477,7 +1476,7 @@ void Cluster::dump_objects(int64_t key_offset, std::string lead) const std::cout << ", Error"; break; } - return false; + return IteratorControl::AdvanceToNext; }); std::cout << std::endl; } diff --git a/src/realm/cluster_tree.cpp b/src/realm/cluster_tree.cpp index 61a27a84ea4..6ad5c199872 100644 --- a/src/realm/cluster_tree.cpp +++ b/src/realm/cluster_tree.cpp @@ -692,7 +692,7 @@ bool ClusterNodeInner::traverse(ClusterTree::TraverseFunction func, int64_t key_ if (child_is_leaf) { Cluster leaf(offs, m_alloc, m_tree_top); leaf.init(mem); - if (func(&leaf)) { + if (func(&leaf) == IteratorControl::Stop) { return true; } } @@ -994,7 +994,7 @@ bool ClusterTree::get_leaf(ObjKey key, ClusterNode::IteratorState& state) const bool ClusterTree::traverse(TraverseFunction func) const { if (m_root->is_leaf()) { - return func(static_cast(m_root.get())); + return func(static_cast(m_root.get())) == IteratorControl::Stop; } else { return static_cast(m_root.get())->traverse(func, 0); @@ -1016,7 +1016,7 @@ void ClusterTree::verify() const #ifdef REALM_DEBUG traverse([](const Cluster* cluster) { cluster->verify(); - return false; + return IteratorControl::AdvanceToNext; }); #endif } diff --git a/src/realm/cluster_tree.hpp b/src/realm/cluster_tree.hpp index e8ffe896a4d..3697d3baff7 100644 --- a/src/realm/cluster_tree.hpp +++ b/src/realm/cluster_tree.hpp @@ -29,9 +29,9 @@ class Cluster; class ClusterTree { public: class Iterator; - using TraverseFunction = util::FunctionRef; + using TraverseFunction = util::FunctionRef; using UpdateFunction = util::FunctionRef; - using ColIterateFunction = util::FunctionRef; + using ColIterateFunction = util::FunctionRef; ClusterTree(Allocator& alloc); virtual ~ClusterTree(); @@ -146,7 +146,7 @@ class ClusterTree { size_t get_ndx(ObjKey k) const noexcept; // Find the leaf containing the requested object bool get_leaf(ObjKey key, ClusterNode::IteratorState& state) const noexcept; - // Visit all leaves and call the supplied function. Stop when function returns true. + // Visit all leaves and call the supplied function. Stop when function returns IteratorControl::Stop. // Not allowed to modify the tree bool traverse(TraverseFunction func) const; // Visit all leaves and call the supplied function. The function can modify the leaf. diff --git a/src/realm/collection.cpp b/src/realm/collection.cpp index 07ab451987b..b52ce9953fa 100644 --- a/src/realm/collection.cpp +++ b/src/realm/collection.cpp @@ -24,14 +24,14 @@ size_t virtual2real(const BPlusTree* tree, size_t ndx) noexcept size_t sz = leaf->size(); for (size_t i = 0; i < sz; i++) { if (i + offset == ndx) { - return true; + return IteratorControl::Stop; } auto k = leaf->get(i); if (k.is_unresolved()) { adjust++; } } - return false; + return IteratorControl::AdvanceToNext; }; tree->traverse(func); @@ -63,7 +63,7 @@ void update_unresolved(std::vector& vec, const BPlusTree* tree) vec.push_back(i + offset); } } - return false; + return IteratorControl::AdvanceToNext; }; tree->traverse(func); diff --git a/src/realm/dictionary.cpp b/src/realm/dictionary.cpp index 458f07d652e..df4876f2445 100644 --- a/src/realm/dictionary.cpp +++ b/src/realm/dictionary.cpp @@ -197,8 +197,7 @@ void DictionaryClusterTree::do_accumulate(size_t* return_ndx, AggregateType& agg } } start_ndx += e; - // Continue - return false; + return IteratorControl::AdvanceToNext; }); if (return_ndx) @@ -375,12 +374,11 @@ size_t Dictionary::find_any(Mixed value) const for (size_t i = 0; i < e; i++) { if (leaf.get(i) == value) { ret = start_ndx + i; - return true; + return IteratorControl::Stop; } } start_ndx += e; - // Continue - return false; + return IteratorControl::AdvanceToNext; }); } diff --git a/src/realm/dictionary.hpp b/src/realm/dictionary.hpp index 7ea1f5762ce..fc96e1a118a 100644 --- a/src/realm/dictionary.hpp +++ b/src/realm/dictionary.hpp @@ -112,8 +112,7 @@ class Dictionary final : public CollectionBaseImpl { for (size_t i = 0; i < e; i++) { f(leaf.get(i)); } - // Continue - return false; + return IteratorControl::AdvanceToNext; }; m_clusters->traverse(trv_func); } @@ -132,8 +131,7 @@ class Dictionary final : public CollectionBaseImpl { for (size_t i = 0; i < e; i++) { f(leaf.get(i)); } - // Continue - return false; + return IteratorControl::AdvanceToNext; }; m_clusters->traverse(trv_func); } diff --git a/src/realm/obj.cpp b/src/realm/obj.cpp index fac418b970f..00d333dbbb1 100644 --- a/src/realm/obj.cpp +++ b/src/realm/obj.cpp @@ -528,9 +528,9 @@ Obj Obj::get_parent_object() const if (get_backlink_cnt(backlink_col_key) == 1) { auto obj_key = get_backlink(backlink_col_key, 0); obj = m_table->get_opposite_table(backlink_col_key)->get_object(obj_key); - return true; + return IteratorControl::Stop; } - return false; + return IteratorControl::AdvanceToNext; }); return obj; @@ -611,7 +611,7 @@ bool Obj::has_backlinks(bool only_strong_links) const } return m_table->for_each_backlink_column([&](ColKey backlink_col_key) { - return get_backlink_cnt(backlink_col_key) != 0; + return get_backlink_cnt(backlink_col_key) != 0 ? IteratorControl::Stop : IteratorControl::AdvanceToNext; }); } @@ -622,7 +622,7 @@ size_t Obj::get_backlink_count() const size_t cnt = 0; m_table->for_each_backlink_column([&](ColKey backlink_col_key) { cnt += get_backlink_cnt(backlink_col_key); - return false; + return IteratorControl::AdvanceToNext; }); return cnt; } @@ -785,9 +785,9 @@ void Obj::traverse_path(Visitor v, PathSizer ps, size_t path_length) const Mixed index = traverser.result(); obj.traverse_path(v, ps, path_length + 1); v(obj, next_col_key, index); - return true; // early out + return IteratorControl::Stop; // early out } - return false; // try next column + return IteratorControl::AdvanceToNext; // try next column }); } else { @@ -2085,7 +2085,7 @@ void Obj::handle_multiple_backlinks_during_schema_migration() migrator.run(); } embedded_obj_tracker->process_pending(); - return false; + return IteratorControl::AdvanceToNext; }; m_table->for_each_backlink_column(copy_links); } @@ -2230,7 +2230,7 @@ void Obj::assign_pk_and_backlinks(const Obj& other) auto copy_links = [this, &other, nb_tombstones](ColKey col) { if (nb_tombstones != m_table->m_tombstones->size()) { // Object has been deleted - we are done - return true; + return IteratorControl::Stop; } auto t = m_table->get_opposite_table(col); auto c = m_table->get_opposite_column(col); @@ -2240,7 +2240,7 @@ void Obj::assign_pk_and_backlinks(const Obj& other) LinkReplacer replacer{linking_obj, c, other, *this}; replacer.run(); } - return false; + return IteratorControl::AdvanceToNext; }; m_table->for_each_backlink_column(copy_links); } diff --git a/src/realm/obj_list.hpp b/src/realm/obj_list.hpp index 293a48e12f6..35f46434f64 100644 --- a/src/realm/obj_list.hpp +++ b/src/realm/obj_list.hpp @@ -76,7 +76,7 @@ class ObjList { auto sz = size(); for (size_t i = 0; i < sz; i++) { auto o = try_get_object(i); - if (o && func(o)) + if (o && func(o) == IteratorControl::Stop) return; } } diff --git a/src/realm/object-store/impl/deep_change_checker.cpp b/src/realm/object-store/impl/deep_change_checker.cpp index 76cf802e34a..4b72972c8ff 100644 --- a/src/realm/object-store/impl/deep_change_checker.cpp +++ b/src/realm/object-store/impl/deep_change_checker.cpp @@ -82,7 +82,7 @@ void DeepChangeChecker::find_related_tables(std::vector& related_t })) { backlinks->backlink_tables.push_back(cur_table->get_link_target(backlink_col_key)->get_key()); } - return false; + return IteratorControl::AdvanceToNext; }); } diff --git a/src/realm/query.cpp b/src/realm/query.cpp index f810d800079..a92dded20e5 100644 --- a/src/realm/query.cpp +++ b/src/realm/query.cpp @@ -992,8 +992,7 @@ void Query::aggregate(QueryStateBase& st, ColKey column_key, size_t* resultcount st.m_key_offset = cluster->get_offset(); st.m_key_values = cluster->get_key_array(); aggregate_internal(node, &st, 0, e, &leaf); - // Continue - return false; + return IteratorControl::AdvanceToNext; }; m_table.unchecked_ptr()->traverse_clusters(f); @@ -1005,7 +1004,7 @@ void Query::aggregate(QueryStateBase& st, ColKey column_key, size_t* resultcount st.m_key_offset = obj.get_key().value; st.match(realm::npos, obj.get(column_key)); } - return false; + return IteratorControl::AdvanceToNext; }); } } @@ -1436,10 +1435,9 @@ ObjKey Query::find() const if (res != not_found) { key = cluster->get_real_key(res); // We should just find one - we're done - return true; + return IteratorControl::Stop; } - // Continue - return false; + return IteratorControl::AdvanceToNext; }; m_table->traverse_clusters(f); @@ -1477,7 +1475,7 @@ void Query::do_find_all(TableView& ret, size_t limit) const refs.add(ObjKey(key_values->get(i) + offset)); --limit; } - return limit == 0; + return limit == 0 ? IteratorControl::Stop : IteratorControl::AdvanceToNext; }; m_table->traverse_clusters(f); @@ -1524,7 +1522,7 @@ void Query::do_find_all(TableView& ret, size_t limit) const st.m_key_values = cluster->get_key_array(); aggregate_internal(node, &st, 0, e, nullptr); // Stop if limit is reached - return st.match_count() == st.limit(); + return st.match_count() == st.limit() ? IteratorControl::Stop : IteratorControl::AdvanceToNext; }; m_table->traverse_clusters(f); @@ -1570,7 +1568,7 @@ size_t Query::do_count(size_t limit) const if (eval_object(obj)) { cnt++; } - return false; + return IteratorControl::AdvanceToNext; }); } else { @@ -1612,7 +1610,7 @@ size_t Query::do_count(size_t limit) const st.m_key_values = cluster->get_key_array(); aggregate_internal(node, &st, 0, e, nullptr); // Stop if limit or end is reached - return st.match_count() == st.limit(); + return st.match_count() == st.limit() ? IteratorControl::Stop : IteratorControl::AdvanceToNext; }; m_table->traverse_clusters(f); diff --git a/src/realm/query_expression.cpp b/src/realm/query_expression.cpp index 6fa50891ffd..0de0b2af0fc 100644 --- a/src/realm/query_expression.cpp +++ b/src/realm/query_expression.cpp @@ -160,8 +160,7 @@ void LinkMap::map_links(size_t column, size_t row, LinkMapFunction& lm) const } } } - // Continue - return false; + return IteratorControl::AdvanceToNext; }); } } @@ -344,8 +343,7 @@ void ColumnDictionaryKeys::evaluate(size_t index, ValueBase& destination) destination.set(n, leaf.get(i)); n++; } - // Continue - return false; + return IteratorControl::AdvanceToNext; }); } } @@ -482,8 +480,7 @@ void Columns::evaluate(size_t index, ValueBase& destination) destination.set(n, leaf.get(i)); n++; } - // Continue - return false; + return IteratorControl::AdvanceToNext; }); } } diff --git a/src/realm/sync/noinst/pending_bootstrap_store.cpp b/src/realm/sync/noinst/pending_bootstrap_store.cpp index f67e4f22567..4d0439ea062 100644 --- a/src/realm/sync/noinst/pending_bootstrap_store.cpp +++ b/src/realm/sync/noinst/pending_bootstrap_store.cpp @@ -138,7 +138,7 @@ void PendingBootstrapStore::add_batch(int64_t query_version, util::Optionaldebug("Clearing incomplete bootstrap for query version %1", obj.get(m_query_version)); - return false; + return IteratorControl::AdvanceToNext; }); incomplete_bootstraps.clear(); diff --git a/src/realm/table.cpp b/src/realm/table.cpp index 1742e6169bc..f337ac17a3f 100644 --- a/src/realm/table.cpp +++ b/src/realm/table.cpp @@ -1104,7 +1104,7 @@ void Table::set_embedded(bool embedded) bool has_backlink_columns = false; for_each_backlink_column([&has_backlink_columns](ColKey) { has_backlink_columns = true; - return true; + return IteratorControl::Stop; }); if (!has_backlink_columns) { throw std::logic_error( @@ -1135,7 +1135,7 @@ void Table::set_embedded(bool embedded) throw std::logic_error( util::format("At least one object in '%1' does have multiple backlinks.", get_name())); } - return false; // continue + return IteratorControl::AdvanceToNext; // continue }); if (backlink_count == 0) { @@ -1554,7 +1554,7 @@ void Table::create_columns() size_t cnt; auto get_column_cnt = [&cnt](const Cluster* cluster) { cnt = cluster->nb_columns(); - return true; + return IteratorControl::Stop; }; traverse_clusters(get_column_cnt); @@ -2008,7 +2008,7 @@ void Table::ensure_graveyard() m_tombstones->init_from_parent(); for_each_and_every_column([ts = m_tombstones.get()](ColKey col) { ts->insert_column(col); - return false; + return IteratorControl::AdvanceToNext; }); } } @@ -2141,7 +2141,7 @@ size_t Table::count_decimal(ColKey col_key, Decimal128 value) const cnt++; } } - return false; + return IteratorControl::AdvanceToNext; }; traverse_clusters(f); @@ -2363,7 +2363,7 @@ Decimal128 Table::maximum_decimal(ColKey col_key, ObjKey* return_ndx) const ret_key = cluster->get_real_key(i); } } - return false; + return IteratorControl::AdvanceToNext; }; traverse_clusters(f); @@ -2421,9 +2421,9 @@ ObjKey Table::find_first(ColKey col_key, T value) const size_t row = leaf.find_first(value, 0, cluster->node_size()); if (row != realm::npos) { key = cluster->get_real_key(row); - return true; + return IteratorControl::Stop; } - return false; + return IteratorControl::AdvanceToNext; }; traverse_clusters(f); @@ -2916,7 +2916,9 @@ bool Table::is_cross_table_link_target() const noexcept auto is_cross_link = [this](ColKey col_key) { auto t = col_key.get_type(); // look for a backlink with a different target than ourselves - return (t == col_type_BackLink && get_opposite_table_key(col_key) != get_key()); + return (t == col_type_BackLink && get_opposite_table_key(col_key) != get_key()) + ? IteratorControl::Stop + : IteratorControl::AdvanceToNext; }; return for_each_backlink_column(is_cross_link); } @@ -3552,9 +3554,9 @@ Table::BacklinkOrigin Table::find_backlink_origin(StringData origin_table_name, if (origin_table->get_name() == origin_table_name && origin_table->get_column_name(origin_link_col) == origin_col_name) { ret = BacklinkOrigin{{origin_table, origin_link_col}}; - return true; + return IteratorControl::Stop; } - return false; + return IteratorControl::AdvanceToNext; }; this->for_each_backlink_column(f); return ret; @@ -3589,7 +3591,7 @@ std::vector> Table::get_incoming_link_columns() cons auto origin_table_key = get_opposite_table_key(backlink_col_key); auto origin_link_col = get_opposite_column(backlink_col_key); origins.emplace_back(origin_table_key, origin_link_col); - return false; + return IteratorControl::AdvanceToNext; }; this->for_each_backlink_column(f); return origins; @@ -3973,10 +3975,10 @@ bool Table::has_any_embedded_objects() auto target_table = get_parent_group()->get_table(target_table_key); if (target_table->is_embedded()) { m_has_any_embedded_objects = true; - return true; // early out + return IteratorControl::Stop; // early out } } - return false; + return IteratorControl::AdvanceToNext; }); } return *m_has_any_embedded_objects; diff --git a/src/realm/table.hpp b/src/realm/table.hpp index dace3ebbbde..01f436e1f8b 100644 --- a/src/realm/table.hpp +++ b/src/realm/table.hpp @@ -474,14 +474,14 @@ class Table { ColKey set_nullability(ColKey col_key, bool nullable, bool throw_on_null); // Iterate through (subset of) columns. The supplied function may abort iteration - // by returning 'true' (early out). + // by returning 'IteratorControl::Stop' (early out). template bool for_each_and_every_column(Func func) const { for (auto col_key : m_leaf_ndx2colkey) { if (!col_key) continue; - if (func(col_key)) + if (func(col_key) == IteratorControl::Stop) return true; } return false; @@ -494,7 +494,7 @@ class Table { continue; if (col_key.get_type() == col_type_BackLink) continue; - if (func(col_key)) + if (func(col_key) == IteratorControl::Stop) return true; } return false; @@ -508,7 +508,7 @@ class Table { continue; if (col_key.get_type() != col_type_BackLink) continue; - if (func(col_key)) + if (func(col_key) == IteratorControl::Stop) return true; } return false; diff --git a/src/realm/table_cluster_tree.cpp b/src/realm/table_cluster_tree.cpp index 645e0884edd..2321c20fa64 100644 --- a/src/realm/table_cluster_tree.cpp +++ b/src/realm/table_cluster_tree.cpp @@ -72,8 +72,7 @@ void TableClusterTree::clear(CascadeState& state) for (size_t i = 0; i < sz; i++) { repl->remove_object(m_owner, cluster->get_real_key(i)); } - // Continue - return false; + return IteratorControl::AdvanceToNext; }); } @@ -102,7 +101,7 @@ void TableClusterTree::enumerate_string_column(ColKey col_key) } } - return false; // Continue + return IteratorControl::AdvanceToNext; }; auto upgrade = [col_key, &keys](Cluster* cluster) { @@ -171,7 +170,7 @@ void TableClusterTree::remove_all_links(CascadeState& state) // Furthermore it is a prerequisite for using 'traverse' that the tree // is not modified if (get_owning_table()->links_to_self(col_key)) { - return false; + return IteratorControl::AdvanceToNext; } auto col_type = col_key.get_type(); if (col_key.is_list() || col_key.is_set()) { @@ -247,8 +246,7 @@ void TableClusterTree::remove_all_links(CascadeState& state) links.push_back(mix.get()); } } - // Continue - return false; + return IteratorControl::AdvanceToNext; }); if (links.size() > 0) { @@ -303,11 +301,10 @@ void TableClusterTree::remove_all_links(CascadeState& state) } } } - return false; + return IteratorControl::AdvanceToNext; }; m_owner->for_each_and_every_column(remove_link_from_column); - // Continue - return false; + return IteratorControl::AdvanceToNext; }; // Go through all clusters diff --git a/src/realm/table_tpl.hpp b/src/realm/table_tpl.hpp index 480a3183acf..ffeafab8022 100644 --- a/src/realm/table_tpl.hpp +++ b/src/realm/table_tpl.hpp @@ -42,8 +42,7 @@ void Table::aggregate(QueryStateBase& st, ColKey column_key) const auto v = leaf.get(local_index); cont = st.match(local_index, v); } - // We should continue - return false; + return IteratorControl::AdvanceToNext; }; traverse_clusters(f); diff --git a/src/realm/table_view.cpp b/src/realm/table_view.cpp index 27592c42725..2a1a74697da 100644 --- a/src/realm/table_view.cpp +++ b/src/realm/table_view.cpp @@ -235,7 +235,7 @@ Timestamp TableView::minmax_timestamp(ColKey column_key, ObjKey* return_key) con best_value = ts; best_key = obj.get_key(); } - return false; + return IteratorControl::AdvanceToNext; }); if (return_key) *return_key = best_key; diff --git a/src/realm/utilities.hpp b/src/realm/utilities.hpp index 26308687399..31d94e1d50e 100644 --- a/src/realm/utilities.hpp +++ b/src/realm/utilities.hpp @@ -393,6 +393,8 @@ typedef int FileDesc; #endif +enum class IteratorControl { AdvanceToNext, Stop }; + } // namespace realm #endif // REALM_UTILITIES_HPP diff --git a/test/test_table.cpp b/test/test_table.cpp index 71738722a04..5f6e7ffb3f3 100644 --- a/test/test_table.cpp +++ b/test/test_table.cpp @@ -3327,7 +3327,7 @@ TEST(Table_object_forward_iterator) size_t tree_size = 0; auto f = [&tree_size](const Cluster* cluster) { tree_size += cluster->node_size(); - return false; + return IteratorControl::AdvanceToNext; }; table.traverse_clusters(f); CHECK_EQUAL(tree_size, size_t(nb_rows)); From c6dd156d406abe3b9786444af7dcf47329f99659 Mon Sep 17 00:00:00 2001 From: Mathias Stearn Date: Wed, 14 Sep 2022 15:50:05 +0200 Subject: [PATCH 12/14] SyncConfig should be default constructible --- src/realm/sync/config.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/realm/sync/config.hpp b/src/realm/sync/config.hpp index a541ee02f7c..7ca8a325e9f 100644 --- a/src/realm/sync/config.hpp +++ b/src/realm/sync/config.hpp @@ -183,6 +183,7 @@ struct SyncConfig { bool simulate_integration_error = false; + SyncConfig() = default; explicit SyncConfig(std::shared_ptr user, bson::Bson partition); explicit SyncConfig(std::shared_ptr user, std::string partition); explicit SyncConfig(std::shared_ptr user, const char* partition); From 5171d30e4a8276b4fb6e7597f783829b1be954fa Mon Sep 17 00:00:00 2001 From: Mathias Stearn Date: Wed, 14 Sep 2022 16:00:17 +0200 Subject: [PATCH 13/14] clean up documentation of internal fields in config structs --- src/realm/object-store/shared_realm.hpp | 16 ++++++++-------- src/realm/object-store/sync/sync_manager.hpp | 2 +- src/realm/sync/config.hpp | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/realm/object-store/shared_realm.hpp b/src/realm/object-store/shared_realm.hpp index de22e231237..806955c7908 100644 --- a/src/realm/object-store/shared_realm.hpp +++ b/src/realm/object-store/shared_realm.hpp @@ -136,9 +136,6 @@ struct RealmConfig { return schema_mode == SchemaMode::ReadOnly; } - // The following are intended for internal/testing purposes and - // should not be publicly exposed in binding APIs - // If false, always return a new Realm instance, and don't return // that Realm instance for other requests for a cached Realm. Useful // for dynamic Realms and for tests that need multiple instances on @@ -149,11 +146,6 @@ struct RealmConfig { // format. Used by the browser to warn the user that it'll modify // the file. bool disable_format_upgrade = false; - // Disable the background worker thread for producing change - // notifications. Useful for tests for those notifications so that - // everything can be done deterministically on one thread, and - // speeds up tests that don't need notifications. - bool automatic_change_notifications = true; // The Scheduler which this Realm should be bound to. If not supplied, // a default one for the current thread will be used. @@ -178,6 +170,14 @@ struct RealmConfig { // delete embedded orphan objects bool automatic_handle_backlicks_in_migrations = false; + + // Only for internal testing. Not to be used by SDKs. + // + // Disable the background worker thread for producing change + // notifications. Useful for tests for those notifications so that + // everything can be done deterministically on one thread, and + // speeds up tests that don't need notifications. + bool automatic_change_notifications = true; }; class Realm : public std::enable_shared_from_this { diff --git a/src/realm/object-store/sync/sync_manager.hpp b/src/realm/object-store/sync/sync_manager.hpp index 05ac9b47136..f0a2aceddb5 100644 --- a/src/realm/object-store/sync/sync_manager.hpp +++ b/src/realm/object-store/sync/sync_manager.hpp @@ -77,7 +77,7 @@ struct SyncClientConfig { LoggerFactory logger_factory; // FIXME: Should probably be util::Logger::Level::error util::Logger::Level log_level = util::Logger::Level::info; - ReconnectMode reconnect_mode = ReconnectMode::normal; + ReconnectMode reconnect_mode = ReconnectMode::normal; // For internal sync-client testing only! bool multiplex_sessions = false; // Optional information about the binding/application that is sent as part of the User-Agent diff --git a/src/realm/sync/config.hpp b/src/realm/sync/config.hpp index 7ca8a325e9f..884ed52e9e2 100644 --- a/src/realm/sync/config.hpp +++ b/src/realm/sync/config.hpp @@ -94,13 +94,13 @@ struct SyncError { using SyncSessionErrorHandler = void(std::shared_ptr, SyncError); enum class ReconnectMode { - /// This is the mode that should always be used in production. In this + /// This is the mode that should always be used by SDKs. In this /// mode the client uses a scheme for determining a reconnect delay that /// prevents it from creating too many connection requests in a short /// amount of time (i.e., a server hammering protection mechanism). normal, - /// For testing purposes only. + /// For internal sync-client testing purposes only. /// /// Never reconnect automatically after the connection is closed due to /// an error. Allow immediate reconnect if the connection was closed From 22dc5dbfda4be29ccc2f188ad1e1965a8c614d9e Mon Sep 17 00:00:00 2001 From: Mathias Stearn Date: Wed, 14 Sep 2022 16:25:39 +0200 Subject: [PATCH 14/14] Stop forcing enums to be 64 bits unnecessarily --- src/realm/object-store/object_schema.hpp | 3 ++- src/realm/object-store/schema.hpp | 2 -- src/realm/object-store/sync/sync_user.hpp | 2 +- src/realm/query_value.hpp | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/realm/object-store/object_schema.hpp b/src/realm/object-store/object_schema.hpp index e0a6861cc7c..4486cfb7b2e 100644 --- a/src/realm/object-store/object_schema.hpp +++ b/src/realm/object-store/object_schema.hpp @@ -32,7 +32,8 @@ class Table; enum class PropertyType : unsigned short; struct ObjectSchemaValidationException; struct Property; -enum SchemaValidationMode : uint64_t; + +enum SchemaValidationMode { Basic = 0, SyncPBS = 1, RejectEmbeddedOrphans = 2, SyncFLX = 4 }; class ObjectSchema { public: diff --git a/src/realm/object-store/schema.hpp b/src/realm/object-store/schema.hpp index 44522882eeb..02a5d27ae2d 100644 --- a/src/realm/object-store/schema.hpp +++ b/src/realm/object-store/schema.hpp @@ -31,8 +31,6 @@ class StringData; struct TableKey; struct Property; -enum SchemaValidationMode : uint64_t { Basic = 0, SyncPBS = 1, RejectEmbeddedOrphans = 2, SyncFLX = 4 }; - // How to handle update_schema() being called on a file which has // already been initialized with a different schema enum class SchemaMode : uint8_t { diff --git a/src/realm/object-store/sync/sync_user.hpp b/src/realm/object-store/sync/sync_user.hpp index 252ab38856e..dff03907e6c 100644 --- a/src/realm/object-store/sync/sync_user.hpp +++ b/src/realm/object-store/sync/sync_user.hpp @@ -191,7 +191,7 @@ class SyncUser : public std::enable_shared_from_this, public Subscriba friend class SyncSession; public: - enum class State : std::size_t { + enum class State { LoggedOut, LoggedIn, Removed, diff --git a/src/realm/query_value.hpp b/src/realm/query_value.hpp index e154070372d..019f0dda9ae 100644 --- a/src/realm/query_value.hpp +++ b/src/realm/query_value.hpp @@ -28,7 +28,7 @@ namespace realm { class TypeOfValue { public: - enum Attribute : int64_t { + enum Attribute { Null = 1, Int = 2, Double = 4,