From a715690449e1fb854a0ad2ab02baf534e1b65fc3 Mon Sep 17 00:00:00 2001 From: reindexer-bot <@> Date: Thu, 19 Sep 2024 06:47:37 +0000 Subject: [PATCH] Merge branch 'go_link_and_warnings' into 'develop' Go link and warnings See merge request itv-backend/reindexer!1691 --- bindings/builtin/reindexer_cgo.h | 4 +- bindings/builtin/symbolizer.go | 2 +- clang-tidy/.clang-tidy | 31 -- clang-tidy/.clang-tidy-ignore | 10 - clang-tidy/run-clang-tidy-18.py | 429 ------------------ .../cmake/modules/RxPrepareInstallFiles.cmake | 7 + .../test/test_storage_compatibility.sh | 195 ++++++++ .../cmd/reindexer_tool/commandsprocessor.cc | 13 +- cpp_src/core/cbinding/reindexer_c.h | 6 +- cpp_src/core/indexdef.cc | 3 +- cpp_src/core/namespacedef.cc | 3 +- .../comparator/comparator_indexed.cc | 56 ++- .../comparator/comparator_indexed.h | 63 +-- .../comparator/comparator_indexed_distinct.h | 21 +- .../comparator/comparator_not_indexed.h | 66 +-- .../comparator_not_indexed_distinct.h | 2 +- .../comparator/equalposition_comparator.cc | 16 +- .../comparator/equalposition_comparator.h | 38 +- .../equalposition_comparator_impl.h | 119 ++--- .../nsselecter/comparator/fieldscomparator.cc | 22 +- .../nsselecter/comparator/fieldscomparator.h | 79 ++-- .../nsselecter/selectiteratorcontainer.cc | 3 +- cpp_src/core/payload/fieldsset.cc | 4 +- cpp_src/core/query/query.h | 5 +- cpp_src/estl/intrusive_ptr.h | 36 +- .../tests/fixtures/item_move_semantics_api.h | 3 +- .../gtests/tests/fixtures/join_selects_api.h | 3 +- .../gtests/tests/unit/csv2jsonconverter.cc | 3 +- cpp_src/replicator/updatesobserver.cc | 3 +- cpp_src/server/cbinding/server_c.h | 2 +- cpp_src/tools/jsontools.cc | 3 +- cpp_src/vendor/gason/gason.cc | 4 +- cpp_src/vendor/gason/gason.h | 4 +- go.mod | 19 +- query.go | 3 + readme.md | 26 +- reflect.go | 79 ++-- reindexer_impl.go | 21 +- test/index_struct_test.go | 145 ++++++ test/uuid_test.go | 12 +- 40 files changed, 716 insertions(+), 847 deletions(-) delete mode 100644 clang-tidy/.clang-tidy delete mode 100644 clang-tidy/.clang-tidy-ignore delete mode 100755 clang-tidy/run-clang-tidy-18.py create mode 100755 cpp_src/cmd/reindexer_server/test/test_storage_compatibility.sh create mode 100644 test/index_struct_test.go diff --git a/bindings/builtin/reindexer_cgo.h b/bindings/builtin/reindexer_cgo.h index a94b0be42..bdca6187c 100644 --- a/bindings/builtin/reindexer_cgo.h +++ b/bindings/builtin/reindexer_cgo.h @@ -4,8 +4,8 @@ extern "C" { #endif -void reindexer_enable_go_logger(); -void reindexer_disable_go_logger(); +void reindexer_enable_go_logger(void); +void reindexer_disable_go_logger(void); #ifdef __cplusplus } diff --git a/bindings/builtin/symbolizer.go b/bindings/builtin/symbolizer.go index 1b9a0f4be..7b80a84b2 100644 --- a/bindings/builtin/symbolizer.go +++ b/bindings/builtin/symbolizer.go @@ -2,7 +2,7 @@ package builtin // extern void cgoTraceback(void*); // extern void cgoSymbolizer(void*); -// extern void cgoSignalsInit(); +// extern void cgoSignalsInit(void); import "C" import ( "os" diff --git a/clang-tidy/.clang-tidy b/clang-tidy/.clang-tidy deleted file mode 100644 index 063df74f1..000000000 --- a/clang-tidy/.clang-tidy +++ /dev/null @@ -1,31 +0,0 @@ -Checks: 'clang-diagnostic-*, - clang-analyzer-*, - performance-*, - bugprone-*, - -bugprone-exception-escape, - -bugprone-branch-clone, - -bugprone-easily-swappable-parameters, - -bugprone-macro-parentheses, - -bugprone-signed-char-misuse, - -bugprone-narrowing-conversions, - -bugprone-reserved-identifier, - -bugprone-implicit-widening-of-multiplication-result, - -bugprone-assignment-in-if-condition, - -bugprone-parent-virtual-call, - -bugprone-integer-division, - -bugprone-unhandled-self-assignment, - -bugprone-inc-dec-in-conditions, - -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, - -performance-no-int-to-ptr, - -performance-enum-size, - -performance-avoid-endl' -# clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling - too many unnecessary warning in vendored code -# performance-no-int-to-ptr - consider how to fix this -# bugprone-macro-parentheses - consider fixing -WarningsAsErrors: '*' -HeaderFilterRegex: '.*(?= 4.0.0 are given under - # the top level key 'Diagnostics' in the output yaml files - mergekey = "Diagnostics" - merged=[] - for replacefile in glob.iglob(os.path.join(tmpdir, '*.yaml')): - content = yaml.safe_load(open(replacefile, 'r')) - if not content: - continue # Skip empty files. - merged.extend(content.get(mergekey, [])) - - if merged: - # MainSourceFile: The key is required by the definition inside - # include/clang/Tooling/ReplacementsYaml.h, but the value - # is actually never used inside clang-apply-replacements, - # so we set it to '' here. - output = {'MainSourceFile': '', mergekey: merged} - with open(mergefile, 'w') as out: - yaml.safe_dump(output, out) - else: - # Empty the file: - open(mergefile, 'w').close() - - -def find_binary(arg, name, build_path): - """Get the path for a binary or exit""" - if arg: - if shutil.which(arg): - return arg - else: - raise SystemExit( - "error: passed binary '{}' was not found or is not executable" - .format(arg)) - - built_path = os.path.join(build_path, "bin", name) - binary = shutil.which(name) or shutil.which(built_path) - if binary: - return binary - else: - raise SystemExit( - "error: failed to find {} in $PATH or at {}" - .format(name, built_path)) - - -def apply_fixes(args, clang_apply_replacements_binary, tmpdir): - """Calls clang-apply-fixes on a given directory.""" - invocation = [clang_apply_replacements_binary] - invocation.append('-ignore-insert-conflict') - if args.format: - invocation.append('-format') - if args.style: - invocation.append('-style=' + args.style) - invocation.append(tmpdir) - subprocess.call(invocation) - - -def run_tidy(args, clang_tidy_binary, tmpdir, build_path, queue, lock, - failed_files): - """Takes filenames out of queue and runs clang-tidy on them.""" - while True: - name = queue.get() - invocation = get_tidy_invocation(name, clang_tidy_binary, args.checks, - tmpdir, build_path, args.header_filter, - args.allow_enabling_alpha_checkers, - args.extra_arg, args.extra_arg_before, - args.quiet, args.config_file, args.config, - args.line_filter, args.use_color, - args.plugins) - - proc = subprocess.Popen(invocation, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - output, err = proc.communicate() - if proc.returncode != 0: - if proc.returncode < 0: - msg = "%s: terminated by signal %d\n" % (name, -proc.returncode) - err += msg.encode('utf-8') - failed_files.append(name) - with lock: - sys.stdout.write(' '.join(invocation) + '\n' + output.decode('utf-8')) - if len(err) > 0: - sys.stdout.flush() - sys.stderr.write(err.decode('utf-8')) - queue.task_done() - - -def main(): - parser = argparse.ArgumentParser(description='Runs clang-tidy over all files ' - 'in a compilation database. Requires ' - 'clang-tidy and clang-apply-replacements in ' - '$PATH or in your build directory.') - parser.add_argument('-allow-enabling-alpha-checkers', - action='store_true', help='allow alpha checkers from ' - 'clang-analyzer.') - parser.add_argument('-clang-tidy-binary', metavar='PATH', - default='clang-tidy-18', - help='path to clang-tidy binary') - parser.add_argument('-clang-apply-replacements-binary', metavar='PATH', - default='clang-apply-replacements-18', - help='path to clang-apply-replacements binary') - parser.add_argument('-checks', default=None, - help='checks filter, when not specified, use clang-tidy ' - 'default') - config_group = parser.add_mutually_exclusive_group() - config_group.add_argument('-config', default=None, - help='Specifies a configuration in YAML/JSON format: ' - ' -config="{Checks: \'*\', ' - ' CheckOptions: {x: y}}" ' - 'When the value is empty, clang-tidy will ' - 'attempt to find a file named .clang-tidy for ' - 'each source file in its parent directories.') - config_group.add_argument('-config-file', default=None, - help='Specify the path of .clang-tidy or custom config ' - 'file: e.g. -config-file=/some/path/myTidyConfigFile. ' - 'This option internally works exactly the same way as ' - '-config option after reading specified config file. ' - 'Use either -config-file or -config, not both.') - parser.add_argument('-header-filter', default=None, - help='regular expression matching the names of the ' - 'headers to output diagnostics from. Diagnostics from ' - 'the main file of each translation unit are always ' - 'displayed.') - parser.add_argument('-line-filter', default=None, - help='List of files with line ranges to filter the' - 'warnings.') - if yaml: - parser.add_argument('-export-fixes', metavar='filename', dest='export_fixes', - help='Create a yaml file to store suggested fixes in, ' - 'which can be applied with clang-apply-replacements.') - parser.add_argument('-j', type=int, default=0, - help='number of tidy instances to be run in parallel.') - parser.add_argument('files', nargs='*', default=['.*'], - help='files to be processed (regex on path)') - parser.add_argument('-fix', action='store_true', help='apply fix-its') - parser.add_argument('-format', action='store_true', help='Reformat code ' - 'after applying fixes') - parser.add_argument('-style', default='file', help='The style of reformat ' - 'code after applying fixes') - parser.add_argument('-use-color', type=strtobool, nargs='?', const=True, - help='Use colors in diagnostics, overriding clang-tidy\'s' - ' default behavior. This option overrides the \'UseColor' - '\' option in .clang-tidy file, if any.') - parser.add_argument('-p', dest='build_path', - help='Path used to read a compile command database.') - parser.add_argument('-extra-arg', dest='extra_arg', - action='append', default=[], - help='Additional argument to append to the compiler ' - 'command line.') - parser.add_argument('-extra-arg-before', dest='extra_arg_before', - action='append', default=[], - help='Additional argument to prepend to the compiler ' - 'command line.') - parser.add_argument('-ignore', default=DEFAULT_CLANG_TIDY_IGNORE, - help='File path to clang-tidy-ignore') - parser.add_argument('-quiet', action='store_true', - help='Run clang-tidy in quiet mode') - parser.add_argument('-load', dest='plugins', - action='append', default=[], - help='Load the specified plugin in clang-tidy.') - args = parser.parse_args() - - db_path = 'compile_commands.json' - - if args.build_path is not None: - build_path = args.build_path - else: - # Find our database - build_path = find_compilation_database(db_path) - - clang_tidy_binary = find_binary(args.clang_tidy_binary, "clang-tidy", - build_path) - - tmpdir = None - if args.fix or (yaml and args.export_fixes): - clang_apply_replacements_binary = find_binary( - args.clang_apply_replacements_binary, "clang-apply-replacements", - build_path) - tmpdir = tempfile.mkdtemp() - - try: - invocation = get_tidy_invocation("", clang_tidy_binary, args.checks, - None, build_path, args.header_filter, - args.allow_enabling_alpha_checkers, - args.extra_arg, args.extra_arg_before, - args.quiet, args.config_file, args.config, - args.line_filter, args.use_color, - args.plugins) - invocation.append('-list-checks') - invocation.append('-') - if args.quiet: - # Even with -quiet we still want to check if we can call clang-tidy. - with open(os.devnull, 'w') as dev_null: - subprocess.check_call(invocation, stdout=dev_null) - else: - subprocess.check_call(invocation) - except: - print("Unable to run clang-tidy.", file=sys.stderr) - sys.exit(1) - - # Load the database and extract all files. - database = json.load(open(os.path.join(build_path, db_path))) - files = set([make_absolute(entry['file'], entry['directory']) - for entry in database]) - files, excluded = filter_files(args.ignore, files) - if excluded: - print("Excluding the following files:\n" + "\n".join(excluded) + "\n") - - max_task = args.j - if max_task == 0: - max_task = multiprocessing.cpu_count() - - # Build up a big regexy filter from all command line arguments. - file_name_re = re.compile('|'.join(args.files)) - - return_code = 0 - try: - # Spin up a bunch of tidy-launching threads. - task_queue = queue.Queue(max_task) - # List of files with a non-zero return code. - failed_files = [] - lock = threading.Lock() - for _ in range(max_task): - t = threading.Thread(target=run_tidy, - args=(args, clang_tidy_binary, tmpdir, build_path, - task_queue, lock, failed_files)) - t.daemon = True - t.start() - - # Fill the queue with files. - for name in files: - if file_name_re.search(name): - task_queue.put(name) - - # Wait for all threads to be done. - task_queue.join() - if len(failed_files): - return_code = 1 - - except KeyboardInterrupt: - # This is a sad hack. Unfortunately subprocess goes - # bonkers with ctrl-c and we start forking merrily. - print('\nCtrl-C detected, goodbye.') - if tmpdir: - shutil.rmtree(tmpdir) - os.kill(0, 9) - - if yaml and args.export_fixes: - print('Writing fixes to ' + args.export_fixes + ' ...') - try: - merge_replacement_files(tmpdir, args.export_fixes) - except: - print('Error exporting fixes.\n', file=sys.stderr) - traceback.print_exc() - return_code=1 - - if args.fix: - print('Applying fixes ...') - try: - apply_fixes(args, clang_apply_replacements_binary, tmpdir) - except: - print('Error applying fixes.\n', file=sys.stderr) - traceback.print_exc() - return_code = 1 - - if tmpdir: - shutil.rmtree(tmpdir) - sys.exit(return_code) - - -if __name__ == '__main__': - main() diff --git a/cpp_src/cmake/modules/RxPrepareInstallFiles.cmake b/cpp_src/cmake/modules/RxPrepareInstallFiles.cmake index 6e7023499..089bb6359 100644 --- a/cpp_src/cmake/modules/RxPrepareInstallFiles.cmake +++ b/cpp_src/cmake/modules/RxPrepareInstallFiles.cmake @@ -14,6 +14,13 @@ endif() function(generate_libs_list INPUT_LIBS OUTPUT_LIST_NAME) set (flibs ${${OUTPUT_LIST_NAME}}) foreach(lib ${INPUT_LIBS}) + get_filename_component(lib_dir ${lib} DIRECTORY) + if (NOT ${lib_dir} STREQUAL "") + set(lib_dir " -L${lib_dir}") + if (NOT (${lib_dir} IN_LIST flibs)) + list(APPEND flibs ${lib_dir}) + endif() + endif() if (${lib} MATCHES "jemalloc" OR ${lib} MATCHES "tcmalloc") elseif(${lib} STREQUAL "-pthread") list(APPEND flibs " -lpthread") diff --git a/cpp_src/cmd/reindexer_server/test/test_storage_compatibility.sh b/cpp_src/cmd/reindexer_server/test/test_storage_compatibility.sh new file mode 100755 index 000000000..d189d3841 --- /dev/null +++ b/cpp_src/cmd/reindexer_server/test/test_storage_compatibility.sh @@ -0,0 +1,195 @@ +#!/bin/bash +# Task: https://github.com/restream/reindexer/-/issues/1188 +set -e + +function KillAndRemoveServer { + local pid=$1 + kill $pid + wait $pid + yum remove -y 'reindexer*' > /dev/null +} + +function WaitForDB { + # wait until DB is loaded + set +e # disable "exit on error" so the script won't stop when DB's not loaded yet + is_connected=$(reindexer_tool --dsn $ADDRESS --command '\databases list'); + while [[ $is_connected != "test" ]] + do + sleep 2 + is_connected=$(reindexer_tool --dsn $ADDRESS --command '\databases list'); + done + set -e +} + +function CompareNamespacesLists { + local ns_list_actual=$1 + local ns_list_expected=$2 + local pid=$3 + + diff=$(echo ${ns_list_actual[@]} ${ns_list_expected[@]} | tr ' ' '\n' | sort | uniq -u) # compare in any order + if [ "$diff" == "" ]; then + echo "## PASS: namespaces list not changed" + else + echo "##### FAIL: namespaces list was changed" + echo "expected: $ns_list_expected" + echo "actual: $ns_list_actual" + KillAndRemoveServer $pid; + exit 1 + fi +} + +function CompareMemstats { + local actual=$1 + local expected=$2 + local pid=$3 + diff=$(echo ${actual[@]} ${expected[@]} | tr ' ' '\n' | sed 's/\(.*\),$/\1/' | sort | uniq -u) # compare in any order + if [ "$diff" == "" ]; then + echo "## PASS: memstats not changed" + else + echo "##### FAIL: memstats was changed" + echo "expected: $expected" + echo "actual: $actual" + KillAndRemoveServer $pid; + exit 1 + fi +} + + +RX_SERVER_CURRENT_VERSION_RPM="$(basename build/reindexer-*server*.rpm)" +VERSION_FROM_RPM=$(echo "$RX_SERVER_CURRENT_VERSION_RPM" | grep -o '.*server-..') +VERSION=$(echo ${VERSION_FROM_RPM: -2:1}) # one-digit version + +echo "## choose latest release rpm file" +if [ $VERSION == 3 ]; then + LATEST_RELEASE=$(python3 cpp_src/cmd/reindexer_server/test/get_last_rx_version.py -v 3) + namespaces_list_expected=$'purchase_options_ext_dict\nchild_account_recommendations\n#config\n#activitystats\nradio_channels\ncollections\n#namespaces\nwp_imports_tasks\nepg_genres\nrecom_media_items_personal\nrecom_epg_archive_default\n#perfstats\nrecom_epg_live_default\nmedia_view_templates\nasset_video_servers\nwp_tasks_schedule\nadmin_roles\n#clientsstats\nrecom_epg_archive_personal\nrecom_media_items_similars\nmenu_items\naccount_recommendations\nkaraoke_items\nmedia_items\nbanners\n#queriesperfstats\nrecom_media_items_default\nrecom_epg_live_personal\nservices\n#memstats\nchannels\nmedia_item_recommendations\nwp_tasks_tasks\nepg' +elif [ $VERSION == 4 ]; then + LATEST_RELEASE=$(python3 cpp_src/cmd/reindexer_server/test/get_last_rx_version.py -v 4) + # replicationstats ns added for v4 + namespaces_list_expected=$'purchase_options_ext_dict\nchild_account_recommendations\n#config\n#activitystats\n#replicationstats\nradio_channels\ncollections\n#namespaces\nwp_imports_tasks\nepg_genres\nrecom_media_items_personal\nrecom_epg_archive_default\n#perfstats\nrecom_epg_live_default\nmedia_view_templates\nasset_video_servers\nwp_tasks_schedule\nadmin_roles\n#clientsstats\nrecom_epg_archive_personal\nrecom_media_items_similars\nmenu_items\naccount_recommendations\nkaraoke_items\nmedia_items\nbanners\n#queriesperfstats\nrecom_media_items_default\nrecom_epg_live_personal\nservices\n#memstats\nchannels\nmedia_item_recommendations\nwp_tasks_tasks\nepg' +else + echo "Unknown version" + exit 1 +fi + +echo "## downloading latest release rpm file: $LATEST_RELEASE" +curl "http://repo.itv.restr.im/itv-api-ng/7/x86_64/$LATEST_RELEASE" --output $LATEST_RELEASE; +echo "## downloading example DB" +curl "https://git.restream.ru/MaksimKravchuk/reindexer_testdata/-/raw/master/big.zip" --output big.zip; +unzip -o big.zip # unzips into mydb_big.rxdump; + +ADDRESS="cproto://127.0.0.1:6534/" +DB_NAME="test" + +memstats_expected=$'[ +{"replication":{"data_hash":24651210926,"data_count":3}}, +{"replication":{"data_hash":6252344969,"data_count":1}}, +{"replication":{"data_hash":37734732881,"data_count":28}}, +{"replication":{"data_hash":0,"data_count":0}}, +{"replication":{"data_hash":1024095024522,"data_count":1145}}, +{"replication":{"data_hash":8373644068,"data_count":1315}}, +{"replication":{"data_hash":0,"data_count":0}}, +{"replication":{"data_hash":0,"data_count":0}}, +{"replication":{"data_hash":0,"data_count":0}}, +{"replication":{"data_hash":0,"data_count":0}}, +{"replication":{"data_hash":7404222244,"data_count":97}}, +{"replication":{"data_hash":94132837196,"data_count":4}}, +{"replication":{"data_hash":1896088071,"data_count":2}}, +{"replication":{"data_hash":0,"data_count":0}}, +{"replication":{"data_hash":-672103903,"data_count":33538}}, +{"replication":{"data_hash":0,"data_count":0}}, +{"replication":{"data_hash":6833710705,"data_count":1}}, +{"replication":{"data_hash":5858155773472,"data_count":4500}}, +{"replication":{"data_hash":-473221280268823592,"data_count":65448}}, +{"replication":{"data_hash":0,"data_count":0}}, +{"replication":{"data_hash":8288213744,"data_count":3}}, +{"replication":{"data_hash":0,"data_count":0}}, +{"replication":{"data_hash":0,"data_count":0}}, +{"replication":{"data_hash":354171024786967,"data_count":3941}}, +{"replication":{"data_hash":-6520334670,"data_count":35886}}, +{"replication":{"data_hash":112772074632,"data_count":281}}, +{"replication":{"data_hash":-12679568198538,"data_count":1623116}} +] +Returned 27 rows' + +echo "##### Forward compatibility test #####" + +DB_PATH=$(pwd)"/rx_db" + +echo "Database: "$DB_PATH + +echo "## installing latest release: $LATEST_RELEASE" +yum install -y $LATEST_RELEASE > /dev/null; +# run RX server with disabled logging +reindexer_server -l warning --httplog=none --rpclog=none --db $DB_PATH & +server_pid=$! +sleep 2; + +reindexer_tool --dsn $ADDRESS$DB_NAME -f mydb_big.rxdump --createdb; +sleep 1; + +namespaces_1=$(reindexer_tool --dsn $ADDRESS$DB_NAME --command '\namespaces list'); +echo $namespaces_1; +CompareNamespacesLists "${namespaces_1[@]}" "${namespaces_list_expected[@]}" $server_pid; + +memstats_1=$(reindexer_tool --dsn $ADDRESS$DB_NAME --command 'select replication.data_hash, replication.data_count from #memstats'); +CompareMemstats "${memstats_1[@]}" "${memstats_expected[@]}" $server_pid; + +KillAndRemoveServer $server_pid; + +echo "## installing current version: $RX_SERVER_CURRENT_VERSION_RPM" +yum install -y build/*.rpm > /dev/null; +reindexer_server -l0 --corelog=none --httplog=none --rpclog=none --db $DB_PATH & +server_pid=$! +sleep 2; + +WaitForDB + +namespaces_2=$(reindexer_tool --dsn $ADDRESS$DB_NAME --command '\namespaces list'); +echo $namespaces_2; +CompareNamespacesLists "${namespaces_2[@]}" "${namespaces_1[@]}" $server_pid; + +memstats_2=$(reindexer_tool --dsn $ADDRESS$DB_NAME --command 'select replication.data_hash, replication.data_count from #memstats'); +CompareMemstats "${memstats_2[@]}" "${memstats_1[@]}" $server_pid; + +KillAndRemoveServer $server_pid; +rm -rf $DB_PATH; +sleep 1; + +echo "##### Backward compatibility test #####" + +echo "## installing current version: $RX_SERVER_CURRENT_VERSION_RPM" +yum install -y build/*.rpm > /dev/null; +reindexer_server -l warning --httplog=none --rpclog=none --db $DB_PATH & +server_pid=$! +sleep 2; + +reindexer_tool --dsn $ADDRESS$DB_NAME -f mydb_big.rxdump --createdb; +sleep 1; + +namespaces_3=$(reindexer_tool --dsn $ADDRESS$DB_NAME --command '\namespaces list'); +echo $namespaces_3; +CompareNamespacesLists "${namespaces_3[@]}" "${namespaces_list_expected[@]}" $server_pid; + +memstats_3=$(reindexer_tool --dsn $ADDRESS$DB_NAME --command 'select replication.data_hash, replication.data_count from #memstats'); +CompareMemstats "${memstats_3[@]}" "${memstats_expected[@]}" $server_pid; + +KillAndRemoveServer $server_pid; + +echo "## installing latest release: $LATEST_RELEASE" +yum install -y $LATEST_RELEASE > /dev/null; +reindexer_server -l warning --httplog=none --rpclog=none --db $DB_PATH & +server_pid=$! +sleep 2; + +WaitForDB + +namespaces_4=$(reindexer_tool --dsn $ADDRESS$DB_NAME --command '\namespaces list'); +echo $namespaces_4; +CompareNamespacesLists "${namespaces_4[@]}" "${namespaces_3[@]}" $server_pid; + +memstats_4=$(reindexer_tool --dsn $ADDRESS$DB_NAME --command 'select replication.data_hash, replication.data_count from #memstats'); +CompareMemstats "${memstats_4[@]}" "${memstats_3[@]}" $server_pid; + +KillAndRemoveServer $server_pid; +rm -rf $DB_PATH; diff --git a/cpp_src/cmd/reindexer_tool/commandsprocessor.cc b/cpp_src/cmd/reindexer_tool/commandsprocessor.cc index 3e1845f7b..98142ab09 100644 --- a/cpp_src/cmd/reindexer_tool/commandsprocessor.cc +++ b/cpp_src/cmd/reindexer_tool/commandsprocessor.cc @@ -30,7 +30,7 @@ Error CommandsProcessor::process(const std::string& command) { template template void CommandsProcessor::setCompletionCallback(T& rx, void (T::*set_completion_callback)(const new_v_callback_t&)) { - (rx.*set_completion_callback)([this](const std::string& input, int) -> replxx::Replxx::completions_t { + (rx.*set_completion_callback)([this](const std::string& input, int) { std::vector completions; const auto err = executor_.GetSuggestions(input, completions); replxx::Replxx::completions_t result; @@ -47,13 +47,16 @@ template template void CommandsProcessor::setCompletionCallback(T& rx, void (T::*set_completion_callback)(const old_v_callback_t&, void*)) { (rx.*set_completion_callback)( - [this](const std::string& input, int, void*) -> replxx::Replxx::completions_t { + [this](const std::string& input, int, void*) { std::vector completions; const auto err = executor_.GetSuggestions(input, completions); - if (!err.ok()) { - return {}; + replxx::Replxx::completions_t result; + if (err.ok()) { + for (const std::string& suggestion : completions) { + result.emplace_back(suggestion); + } } - return completions; + return result; }, nullptr); } diff --git a/cpp_src/core/cbinding/reindexer_c.h b/cpp_src/core/cbinding/reindexer_c.h index cc1fc55c6..6442daf79 100644 --- a/cpp_src/core/cbinding/reindexer_c.h +++ b/cpp_src/core/cbinding/reindexer_c.h @@ -8,7 +8,7 @@ extern "C" { #include "core/type_consts.h" #include "reindexer_ctypes.h" -uintptr_t init_reindexer(); +uintptr_t init_reindexer(void); uintptr_t init_reindexer_with_config(reindexer_config config); void destroy_reindexer(uintptr_t rx); @@ -66,9 +66,9 @@ reindexer_error reindexer_delete_meta(uintptr_t rx, reindexer_string ns, reindex reindexer_error reindexer_cancel_context(reindexer_ctx_info ctx_info, ctx_cancel_type how); void reindexer_enable_logger(void (*logWriter)(int level, char* msg)); -void reindexer_disable_logger(); +void reindexer_disable_logger(void); -void reindexer_init_locale(); +void reindexer_init_locale(void); #ifdef __cplusplus } diff --git a/cpp_src/core/indexdef.cc b/cpp_src/core/indexdef.cc index ad79f4b96..c57cb9a7d 100644 --- a/cpp_src/core/indexdef.cc +++ b/cpp_src/core/indexdef.cc @@ -160,7 +160,8 @@ bool isStore(IndexType type) noexcept { Error IndexDef::FromJSON(span json) { try { - IndexDef::FromJSON(gason::JsonParser().Parse(json)); + gason::JsonParser parser; + IndexDef::FromJSON(parser.Parse(json)); } catch (const gason::Exception& ex) { return Error(errParseJson, "IndexDef: %s", ex.what()); } catch (const Error& err) { diff --git a/cpp_src/core/namespacedef.cc b/cpp_src/core/namespacedef.cc index e06939579..1d6ba0ebf 100644 --- a/cpp_src/core/namespacedef.cc +++ b/cpp_src/core/namespacedef.cc @@ -8,7 +8,8 @@ namespace reindexer { Error NamespaceDef::FromJSON(span json) { try { - FromJSON(gason::JsonParser().Parse(json)); + gason::JsonParser parser; + FromJSON(parser.Parse(json)); } catch (const gason::Exception& ex) { return Error(errParseJson, "NamespaceDef: %s", ex.what()); } catch (const Error& err) { diff --git a/cpp_src/core/nsselecter/comparator/comparator_indexed.cc b/cpp_src/core/nsselecter/comparator/comparator_indexed.cc index 513c81ac8..3adebc35c 100644 --- a/cpp_src/core/nsselecter/comparator/comparator_indexed.cc +++ b/cpp_src/core/nsselecter/comparator/comparator_indexed.cc @@ -85,26 +85,31 @@ typename reindexer::comparators::ValuesHolder::Type } } -template -void initComparator(const reindexer::VariantArray& from, typename reindexer::comparators::ValuesHolder::Type& to) { - if constexpr (Cond == CondSet) { - for (const reindexer::Variant& v : from) { - to.insert(reindexer::comparators::GetValue(v)); - } - } else if constexpr (Cond == CondAllSet) { - int i = 0; - for (const reindexer::Variant& v : from) { - to.values_.emplace(reindexer::comparators::GetValue(v), i); - ++i; - } +template +void initComparatorSet(const reindexer::VariantArray& from, reindexer::comparators::DataHolder& to, SetT&& set) { + for (const reindexer::Variant& v : from) { + set.insert(reindexer::comparators::GetValue(v)); + } + using SetWrpType = typename reindexer::comparators::DataHolder::SetWrpType; + to.setPtr_ = reindexer::make_intrusive(std::forward(set)); +} + +template +void initComparatorAllSet(const reindexer::VariantArray& from, reindexer::comparators::DataHolder& to, SetT&& set) { + int i = 0; + for (const reindexer::Variant& v : from) { + set.values_.emplace(reindexer::comparators::GetValue(v), i); + ++i; } + using AllSetType = typename reindexer::comparators::DataHolder::AllSetType; + to.allSetPtr_ = std::make_unique(std::forward(set)); } template void initComparator(CondType cond, const reindexer::VariantArray& from, reindexer::comparators::DataHolder& to) { using namespace reindexer::comparators; - using SetWrpType = typename DataHolder::SetWrpType; - using AllSetWrpType = typename DataHolder::AllSetWrpType; + using SetType = typename DataHolder::SetType; + using AllSetType = typename DataHolder::AllSetType; switch (cond) { case CondEq: case CondLt: @@ -118,12 +123,10 @@ void initComparator(CondType cond, const reindexer::VariantArray& from, reindexe to.value2_ = GetValue(cond, from, 1); break; case CondSet: - to.setPtr_ = make_intrusive(); - initComparator(from, *to.setPtr_); + initComparatorSet(from, to, SetType{}); break; case CondAllSet: - to.allSetPtr_ = make_intrusive(); - initComparator(from, *to.allSetPtr_); + initComparatorAllSet(from, to, AllSetType{}); break; case CondAny: case CondEmpty: @@ -139,8 +142,6 @@ void initStringComparator(CondType cond, const reindexer::VariantArray& from, re using namespace reindexer::comparators; using SetType = DataHolder::SetType; using AllSetType = DataHolder::AllSetType; - using SetWrpType = DataHolder::SetWrpType; - using AllSetWrpType = DataHolder::AllSetWrpType; switch (cond) { case CondEq: case CondLt: @@ -155,12 +156,10 @@ void initStringComparator(CondType cond, const reindexer::VariantArray& from, re to.value2_ = GetValue(cond, from, 1); break; case CondSet: - to.setPtr_ = make_intrusive(SetType{collate}); - initComparator(from, *to.setPtr_); + initComparatorSet(from, to, SetType{collate}); break; case CondAllSet: - to.allSetPtr_ = make_intrusive(AllSetType{collate, {}}); - initComparator(from, *to.allSetPtr_); + initComparatorAllSet(from, to, AllSetType{collate, {}}); break; case CondAny: case CondEmpty: @@ -499,13 +498,12 @@ ComparatorIndexedComposite::ComparatorIndexedComposite(const VariantArray& value value2_ = GetValue(cond, values, 1); break; case CondSet: - setPtr_ = make_intrusive(SetType{values.size(), reindexer::hash_composite_ref{payloadType, fields}, - reindexer::equal_composite_ref{payloadType, fields}}); - initComparator(values, *setPtr_); + initComparatorSet(values, *this, + SetType{values.size(), reindexer::hash_composite_ref{payloadType, fields}, + reindexer::equal_composite_ref{payloadType, fields}}); break; case CondAllSet: - allSetPtr_ = make_intrusive(AllSetType{{reindexer::PayloadType{payloadType}, reindexer::FieldsSet{fields}}, {}}); - initComparator(values, *allSetPtr_); + initComparatorAllSet(values, *this, AllSetType{{reindexer::PayloadType{payloadType}, reindexer::FieldsSet{fields}}, {}}); break; case CondAny: case CondEmpty: diff --git a/cpp_src/core/nsselecter/comparator/comparator_indexed.h b/cpp_src/core/nsselecter/comparator/comparator_indexed.h index d88509a35..355f82864 100644 --- a/cpp_src/core/nsselecter/comparator/comparator_indexed.h +++ b/cpp_src/core/nsselecter/comparator/comparator_indexed.h @@ -98,17 +98,24 @@ template struct DataHolder { using SingleType = typename ValuesHolder::Type; using SetType = typename ValuesHolder::Type; - using SetWrpType = intrusive_rc_wrapper; + using SetWrpType = const intrusive_atomic_rc_wrapper; // must be const for safe intrusive copying using SetPtrType = intrusive_ptr; using AllSetType = typename ValuesHolder::Type; - using AllSetWrpType = intrusive_rc_wrapper; - using AllSetPtrType = intrusive_ptr; + using AllSetPtrType = std::unique_ptr; DataHolder() noexcept : cond_{CondEq} {} DataHolder(DataHolder&& other) noexcept = default; - DataHolder(const DataHolder& other) = default; + DataHolder(const DataHolder& o) + : cond_{o.cond_}, + value_{o.value_}, + value2_{o.value2_}, + setPtr_{o.setPtr_}, + allSetPtr_{o.allSetPtr_ ? std::make_unique(*o.allSetPtr_) : nullptr} { + // allSetPtr's data are modified during comparison, so we have to make a real copy + } DataHolder& operator=(DataHolder&& other) noexcept = default; - DataHolder& operator=(const DataHolder& other) = default; + DataHolder& operator=(const DataHolder& o) = delete; + CondType cond_; SingleType value_{}; // Either single value or right range boundry SingleType value2_{}; // Left range boundry @@ -837,7 +844,7 @@ class ComparatorIndexedOffsetArrayString : private DataHolder { [[nodiscard]] RX_ALWAYS_INLINE bool Compare(const PayloadValue& item, IdType /*rowId*/) { if (cond_ == CondAllSet) { assertrx_dbg(this->allSetPtr_); - allSetPtr_->allSetValues_.clear(); + this->allSetPtr_->allSetValues_.clear(); } const PayloadFieldValue::Array& arr = *reinterpret_cast(item.Ptr() + offset_); const p_string* ptr = reinterpret_cast(item.Ptr() + arr.offset); @@ -921,7 +928,7 @@ class ComparatorIndexedOffsetArrayStringDistinct : private DataHolderallSetPtr_); - allSetPtr_->allSetValues_.clear(); + this->allSetPtr_->allSetValues_.clear(); } const PayloadFieldValue::Array& arr = *reinterpret_cast(item.Ptr() + offset_); const p_string* ptr = reinterpret_cast(item.Ptr() + arr.offset); @@ -1017,7 +1024,7 @@ class ComparatorIndexedJsonPathString : private DataHolder { [[nodiscard]] RX_ALWAYS_INLINE bool Compare(const PayloadValue& item, IdType /*rowId*/) { if (cond_ == CondAllSet) { assertrx_dbg(this->allSetPtr_); - allSetPtr_->allSetValues_.clear(); + this->allSetPtr_->allSetValues_.clear(); } buffer_.clear(); ConstPayload(payloadType_, item).GetByJsonPath(tagsPath_, buffer_, KeyValueType::String{}); @@ -1105,7 +1112,7 @@ class ComparatorIndexedJsonPathStringDistinct : private DataHolder { [[nodiscard]] RX_ALWAYS_INLINE bool Compare(const PayloadValue& item, IdType /*rowId*/) { if (cond_ == CondAllSet) { assertrx_dbg(this->allSetPtr_); - allSetPtr_->allSetValues_.clear(); + this->allSetPtr_->allSetValues_.clear(); } buffer_.clear(); ConstPayload(payloadType_, item).GetByJsonPath(tagsPath_, buffer_, KeyValueType::String{}); @@ -1242,7 +1249,7 @@ class ComparatorIndexedComposite : private DataHolder { private: // Using pointer for cheap copying and ExpressionTree size reduction - using FieldsSetWrp = intrusive_rc_wrapper; + using FieldsSetWrp = const intrusive_atomic_rc_wrapper; // must be const for safe intrusive copying const CollateOpts* collateOpts_; intrusive_ptr fields_; @@ -1305,13 +1312,13 @@ class ComparatorIndexedOffsetArrayDWithinDistinct { class ComparatorIndexedJsonPathDWithin { public: ComparatorIndexedJsonPathDWithin(const FieldsSet& fields, const PayloadType& payloadType, const VariantArray&); - [[nodiscard]] RX_ALWAYS_INLINE bool Compare(const PayloadValue& item, IdType /*rowId*/) { - buffer_.clear(); - ConstPayload(payloadType_, item).GetByJsonPath(tagsPath_, buffer_, KeyValueType::Double{}); - if rx_unlikely (buffer_.size() != 2) { + [[nodiscard]] RX_ALWAYS_INLINE bool Compare(const PayloadValue& item, IdType /*rowId*/) const { + VariantArray buffer; + ConstPayload(payloadType_, item).GetByJsonPath(tagsPath_, buffer, KeyValueType::Double{}); + if rx_unlikely (buffer.size() != 2) { throw Error(errQueryExec, "DWithin with not point data"); } - return DWithin(Point{buffer_[0].As(), buffer_[1].As()}, point_, distance_); + return DWithin(Point{buffer[0].As(), buffer[1].As()}, point_, distance_); } [[nodiscard]] std::string ConditionStr() const; [[nodiscard]] bool IsDistinct() const noexcept { return false; } @@ -1321,7 +1328,6 @@ class ComparatorIndexedJsonPathDWithin { private: PayloadType payloadType_; TagsPath tagsPath_; - VariantArray buffer_; Point point_; double distance_; }; @@ -1329,24 +1335,24 @@ class ComparatorIndexedJsonPathDWithin { class ComparatorIndexedJsonPathDWithinDistinct { public: ComparatorIndexedJsonPathDWithinDistinct(const FieldsSet& fields, const PayloadType& payloadType, const VariantArray&); - [[nodiscard]] RX_ALWAYS_INLINE bool Compare(const PayloadValue& item, IdType /*rowId*/) { - buffer_.clear(); - ConstPayload(payloadType_, item).GetByJsonPath(tagsPath_, buffer_, KeyValueType::Double{}); - if rx_unlikely (buffer_.size() != 2) { + [[nodiscard]] RX_ALWAYS_INLINE bool Compare(const PayloadValue& item, IdType /*rowId*/) const { + VariantArray buffer; + ConstPayload(payloadType_, item).GetByJsonPath(tagsPath_, buffer, KeyValueType::Double{}); + if rx_unlikely (buffer.size() != 2) { throw Error(errQueryExec, "DWithin with not point data"); } - const Point p{buffer_[0].As(), buffer_[1].As()}; + const Point p{buffer[0].As(), buffer[1].As()}; return DWithin(p, point_, distance_) && distinct_.Compare(p); } [[nodiscard]] std::string ConditionStr() const; [[nodiscard]] bool IsDistinct() const noexcept { return true; } void ExcludeDistinctValues(const PayloadValue& item, IdType /*rowId*/) { - buffer_.clear(); - ConstPayload(payloadType_, item).GetByJsonPath(tagsPath_, buffer_, KeyValueType::Double{}); - if rx_unlikely (buffer_.size() != 2) { + VariantArray buffer; + ConstPayload(payloadType_, item).GetByJsonPath(tagsPath_, buffer, KeyValueType::Double{}); + if rx_unlikely (buffer.size() != 2) { return; } - distinct_.ExcludeValues(Point{buffer_[0].As(), buffer_[1].As()}); + distinct_.ExcludeValues(Point{buffer[0].As(), buffer[1].As()}); } void ClearDistinctValues() noexcept { distinct_.ClearValues(); } @@ -1354,7 +1360,6 @@ class ComparatorIndexedJsonPathDWithinDistinct { ComparatorIndexedDistinct> distinct_; PayloadType payloadType_; TagsPath tagsPath_; - VariantArray buffer_; Point point_; double distance_; }; @@ -1456,10 +1461,10 @@ class ComparatorIndexedOffsetArrayAnyDistinct { void ClearDistinctValues() noexcept { distinct_.ClearValues(); } private: - using ComparatoristincType = std::conditional_t, ComparatorIndexedDistinct>, - ComparatorIndexedDistinct>; + using ComparatorDistinctType = std::conditional_t, ComparatorIndexedDistinct>, + ComparatorIndexedDistinct>; - ComparatoristincType distinct_; + ComparatorDistinctType distinct_; size_t offset_; }; diff --git a/cpp_src/core/nsselecter/comparator/comparator_indexed_distinct.h b/cpp_src/core/nsselecter/comparator/comparator_indexed_distinct.h index 5a0fd6ae9..e43fa7c8b 100644 --- a/cpp_src/core/nsselecter/comparator/comparator_indexed_distinct.h +++ b/cpp_src/core/nsselecter/comparator/comparator_indexed_distinct.h @@ -12,7 +12,12 @@ namespace reindexer { template > class ComparatorIndexedDistinct { public: - ComparatorIndexedDistinct() : values_{make_intrusive()} {} + ComparatorIndexedDistinct() : values_{std::make_unique()} {} + ComparatorIndexedDistinct(ComparatorIndexedDistinct&&) = default; + ComparatorIndexedDistinct& operator=(ComparatorIndexedDistinct&&) = default; + ComparatorIndexedDistinct(const ComparatorIndexedDistinct& o) + : values_{o.values_ ? std::make_unique(*o.values_) : std::make_unique()} {} + [[nodiscard]] RX_ALWAYS_INLINE bool Compare(const T& v) const noexcept { assertrx_dbg(values_); return values_->find(v) == values_->cend(); @@ -27,15 +32,19 @@ class ComparatorIndexedDistinct { } private: - using SetWrpType = intrusive_rc_wrapper; - using SetPtrType = intrusive_ptr; + using SetPtrType = std::unique_ptr; SetPtrType values_; }; class ComparatorIndexedDistinctString { public: - ComparatorIndexedDistinctString(const CollateOpts& collate) : values_{make_intrusive(collate)} {} + ComparatorIndexedDistinctString(const CollateOpts& collate) : values_{std::make_unique(collate)}, collateOpts_{&collate} {} + ComparatorIndexedDistinctString(ComparatorIndexedDistinctString&&) = default; + ComparatorIndexedDistinctString& operator=(ComparatorIndexedDistinctString&&) = default; + ComparatorIndexedDistinctString(const ComparatorIndexedDistinctString& o) + : values_{o.values_ ? std::make_unique(*o.values_) : std::make_unique(*o.collateOpts_)} {} + [[nodiscard]] RX_ALWAYS_INLINE bool Compare(std::string_view str) const noexcept { assertrx_dbg(values_); return values_->find(str) == values_->cend(); @@ -86,10 +95,10 @@ class ComparatorIndexedDistinctString { }; using SetType = string_view_set; - using SetWrpType = intrusive_rc_wrapper; - using SetPtrType = intrusive_ptr; + using SetPtrType = std::unique_ptr; SetPtrType values_; + const CollateOpts* collateOpts_; }; } // namespace reindexer diff --git a/cpp_src/core/nsselecter/comparator/comparator_not_indexed.h b/cpp_src/core/nsselecter/comparator/comparator_not_indexed.h index 097dff18f..e74ee8843 100644 --- a/cpp_src/core/nsselecter/comparator/comparator_not_indexed.h +++ b/cpp_src/core/nsselecter/comparator/comparator_not_indexed.h @@ -18,7 +18,7 @@ template class ComparatorNotIndexedImplBase { protected: ComparatorNotIndexedImplBase(const VariantArray&); - ComparatorNotIndexedImplBase(const ComparatorNotIndexedImplBase&) = delete; + ComparatorNotIndexedImplBase(const ComparatorNotIndexedImplBase&) = default; ComparatorNotIndexedImplBase& operator=(const ComparatorNotIndexedImplBase&) = delete; ComparatorNotIndexedImplBase(ComparatorNotIndexedImplBase&&) = default; ComparatorNotIndexedImplBase& operator=(ComparatorNotIndexedImplBase&&) = default; @@ -48,7 +48,7 @@ template <> class ComparatorNotIndexedImplBase { protected: ComparatorNotIndexedImplBase(const VariantArray&); - ComparatorNotIndexedImplBase(const ComparatorNotIndexedImplBase&) = delete; + ComparatorNotIndexedImplBase(const ComparatorNotIndexedImplBase&) = default; ComparatorNotIndexedImplBase& operator=(const ComparatorNotIndexedImplBase&) = delete; ComparatorNotIndexedImplBase(ComparatorNotIndexedImplBase&&) = default; ComparatorNotIndexedImplBase& operator=(ComparatorNotIndexedImplBase&&) = default; @@ -71,7 +71,7 @@ class ComparatorNotIndexedImplBase { values_.insert(v); } } - ComparatorNotIndexedImplBase(const ComparatorNotIndexedImplBase&) = delete; + ComparatorNotIndexedImplBase(const ComparatorNotIndexedImplBase&) = default; ComparatorNotIndexedImplBase& operator=(const ComparatorNotIndexedImplBase&) = delete; ComparatorNotIndexedImplBase(ComparatorNotIndexedImplBase&&) = default; ComparatorNotIndexedImplBase& operator=(ComparatorNotIndexedImplBase&&) = default; @@ -89,7 +89,7 @@ template <> class ComparatorNotIndexedImplBase { protected: ComparatorNotIndexedImplBase(const VariantArray&); - ComparatorNotIndexedImplBase(const ComparatorNotIndexedImplBase&) = delete; + ComparatorNotIndexedImplBase(const ComparatorNotIndexedImplBase&) = default; ComparatorNotIndexedImplBase& operator=(const ComparatorNotIndexedImplBase&) = delete; ComparatorNotIndexedImplBase(ComparatorNotIndexedImplBase&&) = default; ComparatorNotIndexedImplBase& operator=(ComparatorNotIndexedImplBase&&) = default; @@ -117,7 +117,7 @@ class ComparatorNotIndexedImpl : private ComparatorNotIndexedImplBa public: ComparatorNotIndexedImpl(const VariantArray& values, const PayloadType& payloadType, const TagsPath& fieldPath) : Base{values}, payloadType_{payloadType}, fieldPath_{fieldPath} {} - ComparatorNotIndexedImpl(const ComparatorNotIndexedImpl&) = delete; + ComparatorNotIndexedImpl(const ComparatorNotIndexedImpl&) = default; ComparatorNotIndexedImpl& operator=(const ComparatorNotIndexedImpl&) = delete; ComparatorNotIndexedImpl(ComparatorNotIndexedImpl&&) = default; ComparatorNotIndexedImpl& operator=(ComparatorNotIndexedImpl&&) = default; @@ -149,7 +149,7 @@ class ComparatorNotIndexedImpl : private ComparatorNotIndexedImplBas public: ComparatorNotIndexedImpl(const VariantArray& values, const PayloadType& payloadType, const TagsPath& fieldPath) : Base{values}, distinct_{}, payloadType_{payloadType}, fieldPath_{fieldPath} {} - ComparatorNotIndexedImpl(const ComparatorNotIndexedImpl&) = delete; + ComparatorNotIndexedImpl(const ComparatorNotIndexedImpl&) = default; ComparatorNotIndexedImpl& operator=(const ComparatorNotIndexedImpl&) = delete; ComparatorNotIndexedImpl(ComparatorNotIndexedImpl&&) = default; ComparatorNotIndexedImpl& operator=(ComparatorNotIndexedImpl&&) = default; @@ -185,7 +185,7 @@ class ComparatorNotIndexedImpl { public: ComparatorNotIndexedImpl(const PayloadType& payloadType, const TagsPath& fieldPath) : payloadType_{payloadType}, fieldPath_{fieldPath} {} - ComparatorNotIndexedImpl(const ComparatorNotIndexedImpl&) = delete; + ComparatorNotIndexedImpl(const ComparatorNotIndexedImpl&) = default; ComparatorNotIndexedImpl& operator=(const ComparatorNotIndexedImpl&) = delete; ComparatorNotIndexedImpl(ComparatorNotIndexedImpl&&) = default; ComparatorNotIndexedImpl& operator=(ComparatorNotIndexedImpl&&) = default; @@ -215,7 +215,7 @@ class ComparatorNotIndexedImpl { public: ComparatorNotIndexedImpl(const PayloadType& payloadType, const TagsPath& fieldPath) : payloadType_{payloadType}, fieldPath_{fieldPath} {} - ComparatorNotIndexedImpl(const ComparatorNotIndexedImpl&) = delete; + ComparatorNotIndexedImpl(const ComparatorNotIndexedImpl&) = default; ComparatorNotIndexedImpl& operator=(const ComparatorNotIndexedImpl&) = delete; ComparatorNotIndexedImpl(ComparatorNotIndexedImpl&&) = default; ComparatorNotIndexedImpl& operator=(ComparatorNotIndexedImpl&&) = default; @@ -251,7 +251,7 @@ class ComparatorNotIndexedImpl { public: ComparatorNotIndexedImpl(const PayloadType& payloadType, const TagsPath& fieldPath) : payloadType_{payloadType}, fieldPath_{fieldPath} {} - ComparatorNotIndexedImpl(const ComparatorNotIndexedImpl&) = delete; + ComparatorNotIndexedImpl(const ComparatorNotIndexedImpl&) = default; ComparatorNotIndexedImpl& operator=(const ComparatorNotIndexedImpl&) = delete; ComparatorNotIndexedImpl(ComparatorNotIndexedImpl&&) = default; ComparatorNotIndexedImpl& operator=(ComparatorNotIndexedImpl&&) = default; @@ -282,7 +282,7 @@ class ComparatorNotIndexedImpl : private ComparatorNotIndexedIm public: ComparatorNotIndexedImpl(const PayloadType& payloadType, const TagsPath& fieldPath) : Base{payloadType, fieldPath} {} - ComparatorNotIndexedImpl(const ComparatorNotIndexedImpl&) = delete; + ComparatorNotIndexedImpl(const ComparatorNotIndexedImpl&) = default; ComparatorNotIndexedImpl& operator=(const ComparatorNotIndexedImpl&) = delete; ComparatorNotIndexedImpl(ComparatorNotIndexedImpl&&) = default; ComparatorNotIndexedImpl& operator=(ComparatorNotIndexedImpl&&) = default; @@ -298,18 +298,19 @@ template <> class ComparatorNotIndexedImpl { public: ComparatorNotIndexedImpl(const VariantArray& values, const PayloadType& payloadType, const TagsPath& fieldPath); - ComparatorNotIndexedImpl(const ComparatorNotIndexedImpl&) = delete; + ComparatorNotIndexedImpl(const ComparatorNotIndexedImpl&) = default; ComparatorNotIndexedImpl& operator=(const ComparatorNotIndexedImpl&) = delete; ComparatorNotIndexedImpl(ComparatorNotIndexedImpl&&) = default; ComparatorNotIndexedImpl& operator=(ComparatorNotIndexedImpl&&) = default; [[nodiscard]] std::string ConditionStr() const; [[nodiscard]] RX_ALWAYS_INLINE bool Compare(const PayloadValue& item, IdType /*rowId*/) { - ConstPayload{payloadType_, item}.GetByJsonPath(fieldPath_, buffer_, KeyValueType::Undefined{}); - if (buffer_.size() < 2 || !buffer_[0].Type().IsNumeric() || !buffer_[1].Type().IsNumeric()) { + VariantArray buffer; + ConstPayload{payloadType_, item}.GetByJsonPath(fieldPath_, buffer, KeyValueType::Undefined{}); + if (buffer.size() < 2 || !buffer[0].Type().IsNumeric() || !buffer[1].Type().IsNumeric()) { return false; } - return DWithin(Point{buffer_[0].As(), buffer_[1].As()}, point_, distance_); + return DWithin(Point{buffer[0].As(), buffer[1].As()}, point_, distance_); } [[nodiscard]] bool IsDistinct() const noexcept { return false; } void ClearDistinctValues() const noexcept {} @@ -318,7 +319,6 @@ class ComparatorNotIndexedImpl { protected: PayloadType payloadType_; TagsPath fieldPath_; - VariantArray buffer_; Point point_; double distance_; }; @@ -330,27 +330,29 @@ class ComparatorNotIndexedImpl : private ComparatorNotIndexed public: ComparatorNotIndexedImpl(const VariantArray& values, const PayloadType& payloadType, const TagsPath& fieldPath) : Base{values, payloadType, fieldPath} {} - ComparatorNotIndexedImpl(const ComparatorNotIndexedImpl&) = delete; + ComparatorNotIndexedImpl(const ComparatorNotIndexedImpl&) = default; ComparatorNotIndexedImpl& operator=(const ComparatorNotIndexedImpl&) = delete; ComparatorNotIndexedImpl(ComparatorNotIndexedImpl&&) = default; ComparatorNotIndexedImpl& operator=(ComparatorNotIndexedImpl&&) = default; [[nodiscard]] RX_ALWAYS_INLINE bool Compare(const PayloadValue& item, IdType /*rowId*/) { - ConstPayload{payloadType_, item}.GetByJsonPath(fieldPath_, buffer_, KeyValueType::Undefined{}); - if (buffer_.size() != 2 || !buffer_[0].Type().Is() || !buffer_[0].Type().Is()) { + VariantArray buffer; + ConstPayload{payloadType_, item}.GetByJsonPath(fieldPath_, buffer, KeyValueType::Undefined{}); + if (buffer.size() != 2 || !buffer[0].Type().Is() || !buffer[0].Type().Is()) { return false; } - const Point p{buffer_[0].As(), buffer_[1].As()}; + const Point p{buffer[0].As(), buffer[1].As()}; return DWithin(p, point_, distance_) && distinct_.Compare(Variant{p}); } [[nodiscard]] bool IsDistinct() const noexcept { return true; } void ClearDistinctValues() noexcept { distinct_.ClearValues(); } void ExcludeDistinctValues(const PayloadValue& item, IdType /*rowId*/) { - ConstPayload{payloadType_, item}.GetByJsonPath(fieldPath_, buffer_, KeyValueType::Undefined{}); - if (buffer_.size() != 2 || !buffer_[0].Type().Is() || !buffer_[0].Type().Is()) { + VariantArray buffer; + ConstPayload{payloadType_, item}.GetByJsonPath(fieldPath_, buffer, KeyValueType::Undefined{}); + if (buffer.size() != 2 || !buffer[0].Type().Is() || !buffer[0].Type().Is()) { return; } - const Point p{buffer_[0].As(), buffer_[1].As()}; + const Point p{buffer[0].As(), buffer[1].As()}; distinct_.ExcludeValues(Variant{p}); } using Base::ConditionStr; @@ -370,7 +372,7 @@ class ComparatorNotIndexedImpl { ++i; } } - ComparatorNotIndexedImpl(const ComparatorNotIndexedImpl&) = delete; + ComparatorNotIndexedImpl(const ComparatorNotIndexedImpl&) = default; ComparatorNotIndexedImpl& operator=(const ComparatorNotIndexedImpl&) = delete; ComparatorNotIndexedImpl(ComparatorNotIndexedImpl&&) = default; ComparatorNotIndexedImpl& operator=(ComparatorNotIndexedImpl&&) = default; @@ -411,7 +413,7 @@ class ComparatorNotIndexedImpl : private ComparatorNotIndexedI public: ComparatorNotIndexedImpl(const VariantArray& values, const PayloadType& payloadType, const TagsPath& fieldPath) : Base{values, payloadType, fieldPath} {} - ComparatorNotIndexedImpl(const ComparatorNotIndexedImpl&) = delete; + ComparatorNotIndexedImpl(const ComparatorNotIndexedImpl&) = default; ComparatorNotIndexedImpl& operator=(const ComparatorNotIndexedImpl&) = delete; ComparatorNotIndexedImpl(ComparatorNotIndexedImpl&&) = default; ComparatorNotIndexedImpl& operator=(ComparatorNotIndexedImpl&&) = default; @@ -454,16 +456,24 @@ using ComparatorNotIndexedVariant = std::variant< ComparatorNotIndexedImpl, ComparatorNotIndexedImpl, ComparatorNotIndexedImpl, ComparatorNotIndexedImpl, ComparatorNotIndexedImpl, ComparatorNotIndexedImpl>; -using ComparatorNotIndexedVariantWrp = intrusive_rc_wrapper; - } // namespace comparators class ComparatorNotIndexed { public: ComparatorNotIndexed(std::string_view fieldName, CondType cond, const VariantArray& values, const PayloadType& payloadType, const TagsPath& fieldPath, bool distinct) - : impl_{make_intrusive(createImpl(cond, values, payloadType, fieldPath, distinct))}, + : impl_{std::make_unique(createImpl(cond, values, payloadType, fieldPath, distinct))}, fieldName_{fieldName} {} + ComparatorNotIndexed(ComparatorNotIndexed&&) = default; + ComparatorNotIndexed& operator=(ComparatorNotIndexed&&) = default; + ComparatorNotIndexed(const ComparatorNotIndexed& o) + : impl_{o.impl_ ? std::make_unique(*o.impl_) : nullptr}, + matchedCount_{o.matchedCount_}, + isNotOperation_{o.isNotOperation_}, + fieldName_{o.fieldName_} { + assertrx_throw(o.impl_); + } + [[nodiscard]] const std::string& Name() const& noexcept { return fieldName_; } auto Name() const&& = delete; [[nodiscard]] std::string ConditionStr() const; @@ -676,7 +686,7 @@ class ComparatorNotIndexed { using ImplVariantType = comparators::ComparatorNotIndexedVariant; static ImplVariantType createImpl(CondType, const VariantArray& values, const PayloadType&, const TagsPath&, bool distinct); // Using pointer to reduce ExpressionTree Node size - intrusive_ptr impl_; + std::unique_ptr impl_; int matchedCount_{0}; bool isNotOperation_{false}; std::string fieldName_; diff --git a/cpp_src/core/nsselecter/comparator/comparator_not_indexed_distinct.h b/cpp_src/core/nsselecter/comparator/comparator_not_indexed_distinct.h index fa00b3095..f7e9fc5ab 100644 --- a/cpp_src/core/nsselecter/comparator/comparator_not_indexed_distinct.h +++ b/cpp_src/core/nsselecter/comparator/comparator_not_indexed_distinct.h @@ -8,7 +8,7 @@ namespace reindexer { class ComparatorNotIndexedDistinct { public: ComparatorNotIndexedDistinct() = default; - ComparatorNotIndexedDistinct(const ComparatorNotIndexedDistinct&) = delete; + ComparatorNotIndexedDistinct(const ComparatorNotIndexedDistinct&) = default; ComparatorNotIndexedDistinct& operator=(const ComparatorNotIndexedDistinct&) = delete; ComparatorNotIndexedDistinct(ComparatorNotIndexedDistinct&&) = default; ComparatorNotIndexedDistinct& operator=(ComparatorNotIndexedDistinct&&) = default; diff --git a/cpp_src/core/nsselecter/comparator/equalposition_comparator.cc b/cpp_src/core/nsselecter/comparator/equalposition_comparator.cc index 4cd3e775e..2594a4131 100644 --- a/cpp_src/core/nsselecter/comparator/equalposition_comparator.cc +++ b/cpp_src/core/nsselecter/comparator/equalposition_comparator.cc @@ -6,19 +6,18 @@ namespace reindexer { -void EqualPositionComparatorImpl::BindField(const std::string& name, int field, const VariantArray& values, CondType cond, - const CollateOpts& collate) { +void EqualPositionComparator::BindField(const std::string& name, int field, const VariantArray& values, CondType cond, + const CollateOpts& collate) { bindField(name, field, values, cond, collate); } -void EqualPositionComparatorImpl::BindField(const std::string& name, const FieldsPath& fieldPath, const VariantArray& values, - CondType cond) { +void EqualPositionComparator::BindField(const std::string& name, const FieldsPath& fieldPath, const VariantArray& values, CondType cond) { bindField(name, fieldPath, values, cond, CollateOpts{}); } template -void EqualPositionComparatorImpl::bindField(const std::string& name, F field, const VariantArray& values, CondType cond, - const CollateOpts& collate) { +void EqualPositionComparator::bindField(const std::string& name, F field, const VariantArray& values, CondType cond, + const CollateOpts& collate) { fields_.push_back(field); Context& ctx = ctx_.emplace_back(collate); @@ -34,7 +33,7 @@ void EqualPositionComparatorImpl::bindField(const std::string& name, F field, co name_ += ' ' + name; } -bool EqualPositionComparatorImpl::Compare(const PayloadValue& pv, IdType /*rowId*/) { +bool EqualPositionComparator::Compare(const PayloadValue& pv, IdType /*rowId*/) { ConstPayload pl(payloadType_, pv); size_t len = INT_MAX; @@ -66,14 +65,13 @@ bool EqualPositionComparatorImpl::Compare(const PayloadValue& pv, IdType /*rowId } if (cmpRes) { - ++matchedCount_; return true; } } return false; } -bool EqualPositionComparatorImpl::compareField(size_t field, const Variant& v) { +bool EqualPositionComparator::compareField(size_t field, const Variant& v) { return v.Type().EvaluateOneOf( [&](KeyValueType::Bool) { return ctx_[field].cmpBool.Compare(ctx_[field].cond, static_cast(v)); }, [&](KeyValueType::Int) { return ctx_[field].cmpInt.Compare(ctx_[field].cond, static_cast(v)); }, diff --git a/cpp_src/core/nsselecter/comparator/equalposition_comparator.h b/cpp_src/core/nsselecter/comparator/equalposition_comparator.h index 786f86db8..48a308718 100644 --- a/cpp_src/core/nsselecter/comparator/equalposition_comparator.h +++ b/cpp_src/core/nsselecter/comparator/equalposition_comparator.h @@ -7,13 +7,13 @@ namespace reindexer { -class EqualPositionComparatorImpl : public intrusive_rc_base { +class EqualPositionComparator { public: - EqualPositionComparatorImpl(const PayloadType& payloadType) : payloadType_{payloadType}, name_{"EqualPositions"} {} - EqualPositionComparatorImpl(const EqualPositionComparatorImpl&) = delete; - EqualPositionComparatorImpl(EqualPositionComparatorImpl&&) = delete; - EqualPositionComparatorImpl& operator=(const EqualPositionComparatorImpl&) = delete; - EqualPositionComparatorImpl& operator=(EqualPositionComparatorImpl&&) = delete; + EqualPositionComparator(const PayloadType& payloadType) : payloadType_{payloadType}, name_{"EqualPositions"} {} + EqualPositionComparator(const EqualPositionComparator&) = default; + EqualPositionComparator(EqualPositionComparator&&) = default; + EqualPositionComparator& operator=(const EqualPositionComparator&) = delete; + EqualPositionComparator& operator=(EqualPositionComparator&&) = default; void BindField(const std::string& name, int field, const VariantArray&, CondType, const CollateOpts&); void BindField(const std::string& name, const FieldsPath&, const VariantArray&, CondType); @@ -57,30 +57,4 @@ class EqualPositionComparatorImpl : public intrusive_rc_base { int matchedCount_{0}; }; -class EqualPositionComparator { -public: - EqualPositionComparator(const PayloadType& payloadType) : impl_{make_intrusive(payloadType)} {} - - void BindField(const std::string& name, int field, const VariantArray& values, CondType cond, const CollateOpts& opts) { - return impl_->BindField(name, field, values, cond, opts); - } - void BindField(const std::string& name, const FieldsPath& fields, const VariantArray& values, CondType cond) { - return impl_->BindField(name, fields, values, cond); - } - bool Compare(const PayloadValue& pv, IdType id) { return impl_->Compare(pv, id); } - bool IsBinded() noexcept { return impl_->IsBinded(); } - [[nodiscard]] int GetMatchedCount() const noexcept { return impl_->GetMatchedCount(); } - [[nodiscard]] int FieldsCount() const noexcept { return impl_->FieldsCount(); } - [[nodiscard]] const std::string& Name() const& noexcept { return impl_->Name(); } - [[nodiscard]] const std::string& Dump() const& noexcept { return impl_->Name(); } - [[nodiscard]] double Cost(int expectedIterations) const noexcept { return impl_->Cost(expectedIterations); } - - auto Name() const&& = delete; - auto Dump() const&& = delete; - -private: - // Using pointer to reduce ExpressionTree Node size - intrusive_ptr impl_; -}; - } // namespace reindexer diff --git a/cpp_src/core/nsselecter/comparator/equalposition_comparator_impl.h b/cpp_src/core/nsselecter/comparator/equalposition_comparator_impl.h index e22ba6756..eb31bebbc 100644 --- a/cpp_src/core/nsselecter/comparator/equalposition_comparator_impl.h +++ b/cpp_src/core/nsselecter/comparator/equalposition_comparator_impl.h @@ -1,6 +1,5 @@ #pragma once -#include #include "core/index/string_map.h" #include "core/keyvalue/geometry.h" #include "core/keyvalue/p_string.h" @@ -17,19 +16,9 @@ class EqualPositionComparatorTypeImpl { using AllSetValuesSet = fast_hash_set; public: - EqualPositionComparatorTypeImpl() noexcept = default; - EqualPositionComparatorTypeImpl(const EqualPositionComparatorTypeImpl&) = delete; - EqualPositionComparatorTypeImpl& operator=(const EqualPositionComparatorTypeImpl&) = delete; - EqualPositionComparatorTypeImpl(EqualPositionComparatorTypeImpl&&) = default; - EqualPositionComparatorTypeImpl& operator=(EqualPositionComparatorTypeImpl&&) = default; - void SetValues(CondType cond, const VariantArray& values) { - if (cond == CondSet) { - valuesS_ = std::make_unique(); - } else if (cond == CondAllSet) { - valuesS_ = std::make_unique(); - allSetValuesS_ = std::make_unique(); - } + assertrx_throw(valuesS_.empty()); + assertrx_throw(allSetValuesS_.empty()); for (Variant key : values) { key.Type().EvaluateOneOf([](OneOf) {}, @@ -62,14 +51,14 @@ class EqualPositionComparatorTypeImpl { assertrx_throw(values_.size() == 2); return lhs >= values_[0] && lhs <= values_[1]; case CondSet: - return valuesS_->find(lhs) != valuesS_->end(); + return valuesS_.find(lhs) != valuesS_.end(); case CondAllSet: { - const auto it = valuesS_->find(lhs); - if (it == valuesS_->end()) { + const auto it = valuesS_.find(lhs); + if (it == valuesS_.end()) { return false; } - allSetValuesS_->insert(&*it); - return allSetValuesS_->size() == valuesS_->size(); + allSetValuesS_.insert(&*it); + return allSetValuesS_.size() == valuesS_.size(); } case CondAny: return true; @@ -82,14 +71,11 @@ class EqualPositionComparatorTypeImpl { std::abort(); } - void ClearAllSetValues() { - assertrx_throw(allSetValuesS_); - allSetValuesS_->clear(); - } + void ClearAllSetValues() { allSetValuesS_.clear(); } h_vector values_; - std::unique_ptr valuesS_; - std::unique_ptr allSetValuesS_; + ValuesSet valuesS_; + AllSetValuesSet allSetValuesS_; private: KeyValueType type() { @@ -110,7 +96,7 @@ class EqualPositionComparatorTypeImpl { void addValue(CondType cond, T value) { if (cond == CondSet || cond == CondAllSet) { - valuesS_->emplace(value); + valuesS_.emplace(value); } else { values_.emplace_back(value); } @@ -123,19 +109,9 @@ class EqualPositionComparatorTypeImpl { using AllSetValuesSet = fast_hash_set; public: - EqualPositionComparatorTypeImpl() noexcept = default; - EqualPositionComparatorTypeImpl(const EqualPositionComparatorTypeImpl&) = delete; - EqualPositionComparatorTypeImpl& operator=(const EqualPositionComparatorTypeImpl&) = delete; - EqualPositionComparatorTypeImpl(EqualPositionComparatorTypeImpl&&) = default; - EqualPositionComparatorTypeImpl& operator=(EqualPositionComparatorTypeImpl&&) = default; - void SetValues(CondType cond, const VariantArray& values) { - if (cond == CondSet) { - valuesS_ = std::make_unique(); - } else if (cond == CondAllSet) { - valuesS_ = std::make_unique(); - allSetValuesS_ = std::make_unique(); - } + assertrx_throw(valuesS_.empty()); + assertrx_throw(allSetValuesS_.empty()); for (const Variant& key : values) { key.Type().EvaluateOneOf( @@ -172,14 +148,14 @@ class EqualPositionComparatorTypeImpl { assertrx_throw(values_.size() >= 2); return lhs >= values_[0] && lhs <= values_[1]; case CondSet: - return valuesS_->find(lhs) != valuesS_->end(); + return valuesS_.find(lhs) != valuesS_.end(); case CondAllSet: { - const auto it = valuesS_->find(lhs); - if (it == valuesS_->end()) { + const auto it = valuesS_.find(lhs); + if (it == valuesS_.end()) { return false; } - allSetValuesS_->insert(&*it); - return allSetValuesS_->size() == valuesS_->size(); + allSetValuesS_.insert(&*it); + return allSetValuesS_.size() == valuesS_.size(); } case CondAny: return true; @@ -191,19 +167,16 @@ class EqualPositionComparatorTypeImpl { } std::abort(); } - void ClearAllSetValues() { - assertrx_throw(allSetValuesS_); - allSetValuesS_->clear(); - } + void ClearAllSetValues() { allSetValuesS_.clear(); } h_vector values_; - std::unique_ptr valuesS_; - std::unique_ptr allSetValuesS_; + ValuesSet valuesS_; + AllSetValuesSet allSetValuesS_; private: void addValue(CondType cond, Uuid value) { if (cond == CondSet || cond == CondAllSet) { - valuesS_->emplace(value); + valuesS_.emplace(value); } else { values_.emplace_back(value); } @@ -213,19 +186,15 @@ class EqualPositionComparatorTypeImpl { template <> class EqualPositionComparatorTypeImpl { public: - EqualPositionComparatorTypeImpl(const CollateOpts& collate) : collate_{collate} {} - EqualPositionComparatorTypeImpl(const EqualPositionComparatorTypeImpl&) = delete; - EqualPositionComparatorTypeImpl& operator=(const EqualPositionComparatorTypeImpl&) = delete; + EqualPositionComparatorTypeImpl(const CollateOpts& collate) : valuesS_(collate), collate_{collate} {} + EqualPositionComparatorTypeImpl(const EqualPositionComparatorTypeImpl&) = default; + EqualPositionComparatorTypeImpl& operator=(const EqualPositionComparatorTypeImpl&) = default; EqualPositionComparatorTypeImpl(EqualPositionComparatorTypeImpl&&) = default; EqualPositionComparatorTypeImpl& operator=(EqualPositionComparatorTypeImpl&&) = default; void SetValues(CondType cond, const VariantArray& values) { - if (cond == CondSet) { - valuesS_ = std::make_unique(collate_); - } else if (cond == CondAllSet) { - valuesS_ = std::make_unique(collate_); - allSetValuesS_ = std::make_unique>(); - } + assertrx_throw(valuesS_.empty()); + assertrx_throw(allSetValuesS_.empty()); for (Variant key : values) { key.convert(KeyValueType::String{}); @@ -250,17 +219,14 @@ class EqualPositionComparatorTypeImpl { return (collateCompare(std::string_view(lhs), rhs, collate_) & ComparationResult::Ge) && (collateCompare(std::string_view(lhs), std::string_view(*values_[1]), collate_) & ComparationResult::Le); case CondSet: - assertrx_dbg(valuesS_); - return valuesS_->find(std::string_view(lhs)) != valuesS_->end(); + return valuesS_.find(std::string_view(lhs)) != valuesS_.end(); case CondAllSet: { - assertrx_dbg(valuesS_); - assertrx_dbg(allSetValuesS_); - auto it = valuesS_->find(lhs); - if (it == valuesS_->end()) { + auto it = valuesS_.find(lhs); + if (it == valuesS_.end()) { return false; } - allSetValuesS_->insert(&*it); - return allSetValuesS_->size() == valuesS_->size(); + allSetValuesS_.insert(&*it); + return allSetValuesS_.size() == valuesS_.size(); } case CondAny: return true; @@ -274,10 +240,7 @@ class EqualPositionComparatorTypeImpl { } std::abort(); } - void ClearAllSetValues() { - assertrx_dbg(allSetValuesS_); - allSetValuesS_->clear(); - } + void ClearAllSetValues() { allSetValuesS_.clear(); } h_vector values_; std::string_view cachedValueSV_; @@ -290,13 +253,13 @@ class EqualPositionComparatorTypeImpl { less_key_string(opts)) {} }; - std::unique_ptr valuesS_; - std::unique_ptr> allSetValuesS_; + key_string_set valuesS_; + fast_hash_set allSetValuesS_; private: void addValue(CondType cond, const key_string& value) { if (cond == CondSet || cond == CondAllSet) { - valuesS_->emplace(value); + valuesS_.emplace(value); } else { values_.emplace_back(value); if (values_.size() == 1) { @@ -311,21 +274,11 @@ template <> class EqualPositionComparatorTypeImpl { public: EqualPositionComparatorTypeImpl() = delete; - EqualPositionComparatorTypeImpl(const EqualPositionComparatorTypeImpl&) = delete; - EqualPositionComparatorTypeImpl& operator=(const EqualPositionComparatorTypeImpl&) = delete; - EqualPositionComparatorTypeImpl(EqualPositionComparatorTypeImpl&&) = default; - EqualPositionComparatorTypeImpl& operator=(EqualPositionComparatorTypeImpl&&) = default; }; template <> class EqualPositionComparatorTypeImpl { public: - EqualPositionComparatorTypeImpl() noexcept = default; - EqualPositionComparatorTypeImpl(const EqualPositionComparatorTypeImpl&) = delete; - EqualPositionComparatorTypeImpl& operator=(const EqualPositionComparatorTypeImpl&) = delete; - EqualPositionComparatorTypeImpl(EqualPositionComparatorTypeImpl&&) = default; - EqualPositionComparatorTypeImpl& operator=(EqualPositionComparatorTypeImpl&&) = default; - void SetValues(const VariantArray& values) { if (values.size() != 2) { throw Error(errQueryExec, "CondDWithin expects two arguments"); diff --git a/cpp_src/core/nsselecter/comparator/fieldscomparator.cc b/cpp_src/core/nsselecter/comparator/fieldscomparator.cc index 54edeba82..8f257c4b0 100644 --- a/cpp_src/core/nsselecter/comparator/fieldscomparator.cc +++ b/cpp_src/core/nsselecter/comparator/fieldscomparator.cc @@ -68,7 +68,7 @@ class ArrayAdapter { namespace reindexer { -FieldsComparatorImpl::FieldsComparatorImpl(std::string_view lField, CondType cond, std::string_view rField, PayloadType plType) +FieldsComparator::FieldsComparator(std::string_view lField, CondType cond, std::string_view rField, PayloadType plType) : condition_{cond}, payloadType_{std::move(plType)} { switch (condition_) { case CondEq: @@ -86,14 +86,17 @@ FieldsComparatorImpl::FieldsComparatorImpl(std::string_view lField, CondType con case CondDWithin: throw Error{errQueryExec, "Condition %s is not supported for two field comparing", CondTypeToStr(condition_)}; } + ctx_.resize(1); std::stringstream nameStream; nameStream << lField << ' ' << condition_ << ' ' << rField; name_ = nameStream.str(); } template -bool FieldsComparatorImpl::compare(const LArr& lhs, const RArr& rhs) { +bool FieldsComparator::compare(const LArr& lhs, const RArr& rhs) const { static constexpr bool needCompareTypes{std::is_same_v || std::is_same_v}; + const static CollateOpts kDefaultCollateOpts; + const CollateOpts& collateOpts = collateOpts_ ? *collateOpts_ : kDefaultCollateOpts; switch (condition_) { case CondRange: if (rhs.size() < 2 || rhs[0].Type().template Is() || rhs[1].Type().template Is()) { @@ -105,8 +108,8 @@ bool FieldsComparatorImpl::compare(const LArr& lhs, const RArr& rhs) { continue; } } - if ((v.RelaxCompare(rhs[0], collateOpts_) & ComparationResult::Ge) && - (v.RelaxCompare(rhs[1], collateOpts_) & ComparationResult::Le)) { + if ((v.RelaxCompare(rhs[0], collateOpts) & ComparationResult::Ge) && + (v.RelaxCompare(rhs[1], collateOpts) & ComparationResult::Le)) { return true; } } @@ -138,7 +141,7 @@ bool FieldsComparatorImpl::compare(const LArr& lhs, const RArr& rhs) { continue; } } - if (lv.RelaxCompare(rv, collateOpts_) == ComparationResult::Eq) { + if (lv.RelaxCompare(rv, collateOpts) == ComparationResult::Eq) { found = true; break; } @@ -171,7 +174,7 @@ bool FieldsComparatorImpl::compare(const LArr& lhs, const RArr& rhs) { continue; } } - const auto compRes = lv.RelaxCompare(rv, collateOpts_); + const auto compRes = lv.RelaxCompare(rv, collateOpts); switch (condition_) { case CondEq: case CondSet: @@ -214,7 +217,7 @@ bool FieldsComparatorImpl::compare(const LArr& lhs, const RArr& rhs) { } } -bool FieldsComparatorImpl::compare(const PayloadValue& item, const Context& ctx) { +bool FieldsComparator::compare(const PayloadValue& item, const Context& ctx) const { bool result; if (ctx.lCtx_.fields_.getTagsPathsLength() > 0) { VariantArray lhs; @@ -256,13 +259,10 @@ bool FieldsComparatorImpl::compare(const PayloadValue& item, const Context& ctx) result = compare(ArrayAdapter(item.Ptr() + ctx.lCtx_.offset_, 1, ctx.lCtx_.sizeof_, ctx.lCtx_.type_), ArrayAdapter(item.Ptr() + ctx.rCtx_.offset_, 1, ctx.rCtx_.sizeof_, ctx.rCtx_.type_)); } - if (result) { - ++matchedCount_; - } return result; } -void FieldsComparatorImpl::validateTypes(KeyValueType lType, KeyValueType rType) const { +void FieldsComparator::validateTypes(KeyValueType lType, KeyValueType rType) const { if (lType.IsSame(rType) || lType.Is() || rType.Is()) { return; } diff --git a/cpp_src/core/nsselecter/comparator/fieldscomparator.h b/cpp_src/core/nsselecter/comparator/fieldscomparator.h index d785b48c9..43456120e 100644 --- a/cpp_src/core/nsselecter/comparator/fieldscomparator.h +++ b/cpp_src/core/nsselecter/comparator/fieldscomparator.h @@ -10,24 +10,27 @@ namespace reindexer { -class FieldsComparatorImpl : public intrusive_rc_base { +class FieldsComparator { public: - FieldsComparatorImpl(std::string_view lField, CondType cond, std::string_view rField, PayloadType plType); - FieldsComparatorImpl(const FieldsComparatorImpl&) = delete; - FieldsComparatorImpl(FieldsComparatorImpl&&) = delete; - FieldsComparatorImpl& operator=(const FieldsComparatorImpl&) = delete; - FieldsComparatorImpl& operator=(FieldsComparatorImpl&&) = delete; + FieldsComparator(std::string_view lField, CondType cond, std::string_view rField, PayloadType plType); + FieldsComparator(const FieldsComparator&) = default; + FieldsComparator(FieldsComparator&&) = default; + FieldsComparator& operator=(const FieldsComparator&) = delete; + FieldsComparator& operator=(FieldsComparator&&) = default; bool Compare(const PayloadValue& item, IdType /*rowId*/) { - if (ctx_.size() > 1) { - for (const auto& c : ctx_) { - if (!compare(item, c)) { - return false; - } + if (ctx_.size() == 1) { + const bool res = compare(item, ctx_[0]); + matchedCount_ += int(res); + return res; + } + for (const auto& c : ctx_) { + if (!compare(item, c)) { + return false; } - return true; } - return compare(item, ctx_[0]); + ++matchedCount_; + return true; } double Cost(int expectedIterations) const noexcept { double cost = 1.0; @@ -48,16 +51,18 @@ class FieldsComparatorImpl : public intrusive_rc_base { const std::string& Name() const&& = delete; const std::string& Dump() const& noexcept { return Name(); } const std::string& Dump() const&& = delete; - int GetMatchedCount() const noexcept { return matchedCount_; } void SetLeftField(const FieldsSet& fields) { + assertrx_throw(!leftFieldSet_); setField(fields, ctx_[0].lCtx_); - leftFieldSet = true; + leftFieldSet_ = true; } void SetRightField(const FieldsSet& fields) { - assertrx_dbg(leftFieldSet); + assertrx_throw(leftFieldSet_); setField(fields, ctx_[0].rCtx_); } - void SetLeftField(const FieldsSet& fset, KeyValueType type, bool isArray) { + void SetLeftField(const FieldsSet& fset, KeyValueType type, bool isArray, const CollateOpts& cOpts) { + assertrx_throw(!leftFieldSet_); + collateOpts_ = &cOpts; if (type.Is()) { ctx_.clear(); ctx_.resize(fset.size()); @@ -65,10 +70,10 @@ class FieldsComparatorImpl : public intrusive_rc_base { } else { setField(ctx_[0].lCtx_, fset, type, isArray); } - leftFieldSet = true; + leftFieldSet_ = true; } void SetRightField(const FieldsSet& fset, KeyValueType type, bool isArray) { - assertrx_dbg(leftFieldSet); + assertrx_throw(leftFieldSet_); if ((ctx_.size() > 1) != type.Is()) { throw Error{errQueryExec, "A composite index cannot be compared with a non-composite one: %s", name_}; } @@ -82,7 +87,7 @@ class FieldsComparatorImpl : public intrusive_rc_base { setField(ctx_[0].rCtx_, fset, type, isArray); } } - void SetCollateOpts(const CollateOpts& cOpts) { collateOpts_ = cOpts; } + int GetMatchedCount() const noexcept { return matchedCount_; } private: struct FieldContext { @@ -133,8 +138,8 @@ class FieldsComparatorImpl : public intrusive_rc_base { } } template - bool compare(const LArr& lhs, const RArr& rhs); - bool compare(const PayloadValue& item, const Context&); + bool compare(const LArr& lhs, const RArr& rhs) const; + bool compare(const PayloadValue& item, const Context&) const; void validateTypes(KeyValueType lType, KeyValueType rType) const; inline static bool compareTypes(KeyValueType lType, KeyValueType rType) noexcept { if (lType.IsSame(rType)) { @@ -153,33 +158,11 @@ class FieldsComparatorImpl : public intrusive_rc_base { std::string name_; CondType condition_; + int matchedCount_{0}; PayloadType payloadType_; - CollateOpts collateOpts_; - h_vector ctx_{Context{}}; - int matchedCount_ = 0; - bool leftFieldSet = false; -}; - -class FieldsComparator { -public: - FieldsComparator(std::string_view lField, CondType cond, std::string_view rField, PayloadType plType) - : impl_{make_intrusive(lField, cond, rField, plType)} {} - bool Compare(const PayloadValue& item, IdType rowId) { return impl_->Compare(item, rowId); } - double Cost(int expectedIterations) const noexcept { return impl_->Cost(expectedIterations); } - const std::string& Name() const& noexcept { return impl_->Name(); } - const std::string& Name() const&& = delete; - const std::string& Dump() const& noexcept { return impl_->Dump(); } - const std::string& Dump() const&& = delete; - int GetMatchedCount() const noexcept { return impl_->GetMatchedCount(); } - void SetLeftField(const FieldsSet& fields) { return impl_->SetLeftField(fields); } - void SetRightField(const FieldsSet& fields) { return impl_->SetRightField(fields); } - void SetLeftField(const FieldsSet& fset, KeyValueType type, bool isArray) { return impl_->SetLeftField(fset, type, isArray); } - void SetRightField(const FieldsSet& fset, KeyValueType type, bool isArray) { return impl_->SetRightField(fset, type, isArray); } - void SetCollateOpts(const CollateOpts& cOpts) { impl_->SetCollateOpts(cOpts); } - -private: - // Using pointer to reduce ExpressionTree Node size - intrusive_ptr impl_; + const CollateOpts* collateOpts_{nullptr}; + std::vector ctx_; + bool leftFieldSet_{false}; }; } // namespace reindexer diff --git a/cpp_src/core/nsselecter/selectiteratorcontainer.cc b/cpp_src/core/nsselecter/selectiteratorcontainer.cc index fb3219902..dcdfe7428 100644 --- a/cpp_src/core/nsselecter/selectiteratorcontainer.cc +++ b/cpp_src/core/nsselecter/selectiteratorcontainer.cc @@ -261,8 +261,7 @@ void SelectIteratorContainer::processField(FieldsComparator& fc, const QueryFiel if (field.IsFieldIndexed()) { auto& index = ns.indexes_[field.IndexNo()]; if constexpr (left) { - fc.SetCollateOpts(index->Opts().collateOpts_); - fc.SetLeftField(field.Fields(), field.FieldType(), index->Opts().IsArray()); + fc.SetLeftField(field.Fields(), field.FieldType(), index->Opts().IsArray(), index->Opts().collateOpts_); } else { fc.SetRightField(field.Fields(), field.FieldType(), index->Opts().IsArray()); } diff --git a/cpp_src/core/payload/fieldsset.cc b/cpp_src/core/payload/fieldsset.cc index 3a2dfeee7..19b34fd69 100644 --- a/cpp_src/core/payload/fieldsset.cc +++ b/cpp_src/core/payload/fieldsset.cc @@ -9,7 +9,9 @@ namespace reindexer { FieldsSet::FieldsSet(const TagsMatcher& tagsMatcher, const h_vector& fields) : mask_(0) { for (const std::string& str : fields) { - tagsPaths_.emplace_back(tagsMatcher.path2tag(str)); + if (auto tagsPath = tagsMatcher.path2tag(str); !tagsPath.empty()) { + tagsPaths_.emplace_back(std::move(tagsPath)); + } } } diff --git a/cpp_src/core/query/query.h b/cpp_src/core/query/query.h index ca459bd63..7e8f9bbd6 100644 --- a/cpp_src/core/query/query.h +++ b/cpp_src/core/query/query.h @@ -667,6 +667,9 @@ class Query { } /// Sets list of columns in this namespace to be finally selected. + /// The columns should be specified in the same case as the jsonpaths corresponding to them. + /// Non-existent fields and fields in the wrong case are ignored. + /// If there are no fields in this list that meet these conditions, then the filter works as "*". /// @param l - list of columns to be selected. template >* = nullptr> Query& Select(std::initializer_list l) & { @@ -686,7 +689,7 @@ class Query { selectFilter_.insert(selectFilter_.begin(), l.begin(), l.end()); selectFilter_.erase( std::remove_if(selectFilter_.begin(), selectFilter_.end(), [](const auto& s) { return s == "*"sv || s.empty(); }), - selectFilter_.end()); + selectFilter_.end()); return *this; } template diff --git a/cpp_src/estl/intrusive_ptr.h b/cpp_src/estl/intrusive_ptr.h index 512390be9..4c618512b 100644 --- a/cpp_src/estl/intrusive_ptr.h +++ b/cpp_src/estl/intrusive_ptr.h @@ -165,21 +165,21 @@ template class intrusive_atomic_rc_wrapper; template -inline void intrusive_ptr_add_ref(intrusive_atomic_rc_wrapper* x) noexcept { +inline void intrusive_ptr_add_ref(const intrusive_atomic_rc_wrapper* x) noexcept { if (x) { x->refcount.fetch_add(1, std::memory_order_relaxed); } } template -inline void intrusive_ptr_release(intrusive_atomic_rc_wrapper* x) noexcept { +inline void intrusive_ptr_release(const intrusive_atomic_rc_wrapper* x) noexcept { if (x && x->refcount.fetch_sub(1, std::memory_order_acq_rel) == 1) { delete x; } } template -inline bool intrusive_ptr_is_unique(intrusive_atomic_rc_wrapper* x) noexcept { +inline bool intrusive_ptr_is_unique(const intrusive_atomic_rc_wrapper* x) noexcept { // std::memory_order_acquire - is essential for COW constructions based on intrusive_ptr return !x || (x->refcount.load(std::memory_order_acquire) == 1); } @@ -191,12 +191,12 @@ class intrusive_atomic_rc_wrapper : public T { intrusive_atomic_rc_wrapper(Args&&... args) : T(std::forward(args)...) {} intrusive_atomic_rc_wrapper& operator=(const intrusive_atomic_rc_wrapper&) = delete; -protected: - std::atomic refcount{0}; +private: + mutable std::atomic refcount{0}; - friend void intrusive_ptr_add_ref<>(intrusive_atomic_rc_wrapper* x) noexcept; - friend void intrusive_ptr_release<>(intrusive_atomic_rc_wrapper* x) noexcept; - friend bool intrusive_ptr_is_unique<>(intrusive_atomic_rc_wrapper* x) noexcept; + friend void intrusive_ptr_add_ref<>(const intrusive_atomic_rc_wrapper* x) noexcept; + friend void intrusive_ptr_release<>(const intrusive_atomic_rc_wrapper* x) noexcept; + friend bool intrusive_ptr_is_unique<>(const intrusive_atomic_rc_wrapper* x) noexcept; }; template @@ -228,7 +228,7 @@ class intrusive_rc_wrapper : public T { intrusive_rc_wrapper(Args&&... args) : T(std::forward(args)...) {} intrusive_rc_wrapper& operator=(const intrusive_rc_wrapper&) = delete; -protected: +private: int refcount{0}; friend void intrusive_ptr_add_ref<>(intrusive_rc_wrapper* x) noexcept; @@ -241,27 +241,27 @@ class intrusive_atomic_rc_base { intrusive_atomic_rc_base& operator=(const intrusive_atomic_rc_base&) = delete; virtual ~intrusive_atomic_rc_base() = default; -protected: - std::atomic refcount{0}; +private: + mutable std::atomic refcount{0}; - friend void intrusive_ptr_add_ref(intrusive_atomic_rc_base* x) noexcept; - friend void intrusive_ptr_release(intrusive_atomic_rc_base* x) noexcept; - friend bool intrusive_ptr_is_unique(intrusive_atomic_rc_base* x) noexcept; + friend void intrusive_ptr_add_ref(const intrusive_atomic_rc_base* x) noexcept; + friend void intrusive_ptr_release(const intrusive_atomic_rc_base* x) noexcept; + friend bool intrusive_ptr_is_unique(const intrusive_atomic_rc_base* x) noexcept; }; -inline void intrusive_ptr_add_ref(intrusive_atomic_rc_base* x) noexcept { +inline void intrusive_ptr_add_ref(const intrusive_atomic_rc_base* x) noexcept { if (x) { x->refcount.fetch_add(1, std::memory_order_relaxed); } } -inline void intrusive_ptr_release(intrusive_atomic_rc_base* x) noexcept { +inline void intrusive_ptr_release(const intrusive_atomic_rc_base* x) noexcept { if (x && x->refcount.fetch_sub(1, std::memory_order_acq_rel) == 1) { delete x; } } -inline bool intrusive_ptr_is_unique(intrusive_atomic_rc_base* x) noexcept { +inline bool intrusive_ptr_is_unique(const intrusive_atomic_rc_base* x) noexcept { // std::memory_order_acquire - is essential for COW constructions based on intrusive_ptr return !x || (x->refcount.load(std::memory_order_acquire) == 1); } @@ -271,7 +271,7 @@ class intrusive_rc_base { intrusive_rc_base& operator=(const intrusive_rc_base&) = delete; virtual ~intrusive_rc_base() = default; -protected: +private: int refcount{0}; friend void intrusive_ptr_add_ref(intrusive_rc_base* x) noexcept; diff --git a/cpp_src/gtests/tests/fixtures/item_move_semantics_api.h b/cpp_src/gtests/tests/fixtures/item_move_semantics_api.h index e60acac2f..b6c7bea47 100644 --- a/cpp_src/gtests/tests/fixtures/item_move_semantics_api.h +++ b/cpp_src/gtests/tests/fixtures/item_move_semantics_api.h @@ -51,7 +51,8 @@ class ItemMoveSemanticsApi : public ReindexerApi { auto&& item = pair.second; Error err = rt.reindexer->Upsert(default_namespace, item); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_NO_THROW(gason::JsonParser().Parse(item.GetJSON())); + gason::JsonParser parser; + ASSERT_NO_THROW(parser.Parse(item.GetJSON())); } const auto err = rt.reindexer->Commit(default_namespace); ASSERT_TRUE(err.ok()) << err.what(); diff --git a/cpp_src/gtests/tests/fixtures/join_selects_api.h b/cpp_src/gtests/tests/fixtures/join_selects_api.h index b58ed111d..a26d6d2ab 100644 --- a/cpp_src/gtests/tests/fixtures/join_selects_api.h +++ b/cpp_src/gtests/tests/fixtures/join_selects_api.h @@ -219,7 +219,8 @@ class JoinSelectsApi : public ReindexerApi { if (!err.ok()) { break; } - gason::JsonParser().Parse(reindexer::giftStr(wrSer.Slice())); + gason::JsonParser parser; + parser.Parse(reindexer::giftStr(wrSer.Slice())); } } catch (const gason::Exception& ex) { return Error(errParseJson, "VerifyResJSON: %s", ex.what()); diff --git a/cpp_src/gtests/tests/unit/csv2jsonconverter.cc b/cpp_src/gtests/tests/unit/csv2jsonconverter.cc index ae407e276..03f34b327 100644 --- a/cpp_src/gtests/tests/unit/csv2jsonconverter.cc +++ b/cpp_src/gtests/tests/unit/csv2jsonconverter.cc @@ -70,7 +70,8 @@ std::string csv2json(std::string_view row, const std::vector& schem for (size_t i = 0; i < fields.size(); ++i) { if (!fields[i].empty()) { try { - gason::JsonParser().Parse(std::string_view{fields[i]}); + gason::JsonParser parser; + parser.Parse(std::string_view{fields[i]}); builder.Raw(schema[i], fields[i]); } catch (const gason::Exception&) { builder.Raw(schema[i], '"' + fields[i] + '"'); diff --git a/cpp_src/replicator/updatesobserver.cc b/cpp_src/replicator/updatesobserver.cc index b0e6ba47f..77083083e 100644 --- a/cpp_src/replicator/updatesobserver.cc +++ b/cpp_src/replicator/updatesobserver.cc @@ -67,7 +67,8 @@ bool UpdatesFilters::Check(std::string_view ns) const { Error UpdatesFilters::FromJSON(span json) { try { - FromJSON(gason::JsonParser().Parse(json)); + gason::JsonParser parser; + FromJSON(parser.Parse(json)); } catch (const gason::Exception& ex) { return Error(errParseJson, "UpdatesFilter: %s", ex.what()); } catch (const Error& err) { diff --git a/cpp_src/server/cbinding/server_c.h b/cpp_src/server/cbinding/server_c.h index ef4959fcf..058262126 100644 --- a/cpp_src/server/cbinding/server_c.h +++ b/cpp_src/server/cbinding/server_c.h @@ -7,7 +7,7 @@ extern "C" { #include "core/cbinding/reindexer_ctypes.h" -uintptr_t init_reindexer_server(); +uintptr_t init_reindexer_server(void); void destroy_reindexer_server(uintptr_t psvc); reindexer_error start_reindexer_server(uintptr_t psvc, reindexer_string config); reindexer_error stop_reindexer_server(uintptr_t psvc); diff --git a/cpp_src/tools/jsontools.cc b/cpp_src/tools/jsontools.cc index a8fc8996e..a9ceebff8 100644 --- a/cpp_src/tools/jsontools.cc +++ b/cpp_src/tools/jsontools.cc @@ -88,7 +88,8 @@ void jsonValueToString(gason::JsonValue o, WrSerializer& ser, int shift, int ind } void prettyPrintJSON(span json, WrSerializer& ser, int shift) { - jsonValueToString(gason::JsonParser().Parse(json).value, ser, shift, 0); + gason::JsonParser parser; + jsonValueToString(parser.Parse(json).value, ser, shift, 0); } std::string stringifyJson(const gason::JsonNode& elem) { diff --git a/cpp_src/vendor/gason/gason.cc b/cpp_src/vendor/gason/gason.cc index db4bdb701..6fd42c1f5 100644 --- a/cpp_src/vendor/gason/gason.cc +++ b/cpp_src/vendor/gason/gason.cc @@ -404,7 +404,7 @@ const JsonNode& JsonNode::operator[](std::string_view key) const { return empty_node; } -JsonNode JsonParser::Parse(span str, size_t* length) { +JsonNode JsonParser::Parse(span str, size_t* length) & { largeStrings_->clear(); char* endp = nullptr; JsonNode val{{}, nullptr, {}}; @@ -420,7 +420,7 @@ JsonNode JsonParser::Parse(span str, size_t* length) { return val; } -JsonNode JsonParser::Parse(std::string_view str, size_t* length) { +JsonNode JsonParser::Parse(std::string_view str, size_t* length) & { tmp_.reserve(str.size()); tmp_.assign(str.begin(), str.end()); return Parse(span(&tmp_[0], tmp_.size()), length); diff --git a/cpp_src/vendor/gason/gason.h b/cpp_src/vendor/gason/gason.h index 77899bb6c..50808347d 100644 --- a/cpp_src/vendor/gason/gason.h +++ b/cpp_src/vendor/gason/gason.h @@ -257,9 +257,9 @@ class JsonParser { public: JsonParser(LargeStringStorageT* strings = nullptr) : largeStrings_(strings ? strings : &internalLargeStrings_) {} // Inplace parse. Buffer pointed by str will be changed - JsonNode Parse(span str, size_t* length = nullptr); + JsonNode Parse(span str, size_t* length = nullptr) &; // Copy str. Buffer pointed by str will be copied - JsonNode Parse(std::string_view str, size_t* length = nullptr); + JsonNode Parse(std::string_view str, size_t* length = nullptr) &; private: JsonAllocator alloc_; diff --git a/go.mod b/go.mod index 40eafb6f8..eb2412df5 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/restream/reindexer/v3 -go 1.15 +go 1.17 require ( github.com/golang/snappy v0.0.4 @@ -12,3 +12,20 @@ require ( go.opentelemetry.io/otel/trace v1.14.0 gopkg.in/yaml.v2 v2.4.0 ) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.32.1 // indirect + github.com/prometheus/procfs v0.7.3 // indirect + golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect + google.golang.org/protobuf v1.26.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/query.go b/query.go index be5643f4c..f6aed23f7 100644 --- a/query.go +++ b/query.go @@ -1188,6 +1188,9 @@ func (q *Query) On(index string, condition int, joinIndex string) *Query { } // Select add filter to fields of result's objects +// The `fields` should be specified in the same case as the jsonpaths corresponding to them. +// Non-existent `fields` and `fields` in the wrong case are ignored. +// If there are no `fields` in this list that meet these conditions, then the filter works as "*". func (q *Query) Select(fields ...string) *Query { for _, field := range fields { q.ser.PutVarCUInt(querySelectFilter).PutVString(field) diff --git a/readme.md b/readme.md index 93e615986..4680cc482 100644 --- a/readme.md +++ b/readme.md @@ -171,7 +171,7 @@ import ( ) -// Define struct with reindex tags +// Define struct with reindex tags. Fields must be exported - private fields can not be written into reindexer type Item struct { ID int64 `reindex:"id,,pk"` // 'id' is primary key Name string `reindex:"name"` // add index by 'name' field @@ -457,10 +457,12 @@ Fields with regular indexes are not nullable. Condition `is NULL` is supported o ### Nested Structs By default, Reindexer scans all nested structs and adds their fields to the namespace (as well as indexes specified). +During indexes scan private (unexported fields), fields tagged with `reindex:"-"` and fields tagged with `json:"-"` will be skipped. ```go type Actor struct { Name string `reindex:"actor_name"` + Age int `reindex:"age"` } type BaseItem struct { @@ -469,13 +471,16 @@ type BaseItem struct { } type ComplexItem struct { - BaseItem // Index fields of BaseItem will be added to reindex - Actor []Actor // Index fields of Actor will be added to reindex as arrays - Name string `reindex:"name"` // Hash-index for "name" - Year int `reindex:"year,tree"` // Tree-index for "year" - Value int `reindex:"value,-"` // Store(column)-index for "value" - Metainfo int `json:"-"` // Field "MetaInfo" will not be stored in reindexer - Parent *Item `reindex:"-"` // Index fields of parent will NOT be added to reindex + BaseItem // Index fields of BaseItem will be added to reindex + Actor []Actor // Index fields ("name" and "age") of Actor will be added to reindex as array-indexes + Name string `reindex:"name"` // Hash-index for "name" + Year int `reindex:"year,tree"` // Tree-index for "year" + Value int `reindex:"value,-"` // Store(column)-index for "value" + Metainfo int `json:"-"` // Field "MetaInfo" will not be stored in reindexer + Parent *Item `reindex:"-"` // Index fields of "Parent" will NOT be added to reindex and all of the "Parent" exported content will be stored as non-indexed data + ParentHidden *Item `json:"-"` // Indexes and fields of "ParentHidden" will NOT be added to reindexer + privateParent *Item // Indexes and fields of "ParentHidden" will NOT be added to reindexer (same as with `json:"-"`) + AnotherActor Actor `reindex:"actor"` // Index fields of "AnotherActor" will be added to reindexer with prefix "actor." (in this example two indexes will be created: "actor.actor_name" and "actor.age") } ``` @@ -895,6 +900,7 @@ type Actor struct { IsVisible bool `reindex:"is_visible"` } +// Fields, marked with 'joined' must also be exported - otherwise reindexer's binding will not be able to put data in those fields type ItemWithJoin struct { ID int `reindex:"id"` Name string `reindex:"name"` @@ -1334,7 +1340,7 @@ In case of requirement to serialize results of Query in JSON format, then it is ```go ... iterator := db.Query("items"). - Select ("id","name"). // Filter output JSON: Select only "id" and "name" fields of items, another fields will be omitted + Select ("id","name"). // Filter output JSON: Select only "id" and "name" fields of items, another fields will be omitted. This fields should be specified in the same case as the jsonpaths corresponding to them. Limit (1). ExecToJson ("root_object") // Name of root object of output JSON @@ -1409,7 +1415,7 @@ WARNING: when used `AllowUnsafe(true)` queries returns shared pointers to struct #### Limit size of object cache -By default, maximum size of object cache is 256000 items for each namespace. To change maximum size use `ObjCacheSize` method of `NameapaceOptions`, passed +By default, maximum size of object cache is 256000 items for each namespace. To change maximum size use `ObjCacheSize` method of `NamespaceOptions`, passed to OpenNamespace. e.g. ```go diff --git a/reflect.go b/reflect.go index 7b1394878..e23197a69 100644 --- a/reflect.go +++ b/reflect.go @@ -35,6 +35,8 @@ type indexOptions struct { isSparse bool rtreeType string isUuid bool + isJoined bool + isComposite bool } func parseRxTags(field reflect.StructField) (idxName string, idxType string, expireAfter string, idxSettings []string) { @@ -56,7 +58,7 @@ func parseRxTags(field reflect.StructField) (idxName string, idxType string, exp return } -func parseIndexes(namespace string, st reflect.Type, joined *map[string][]int) (indexDefs []bindings.IndexDef, err error) { +func parseIndexes(st reflect.Type, joined *map[string][]int) (indexDefs []bindings.IndexDef, err error) { if err = parseIndexesImpl(&indexDefs, st, false, "", "", joined, nil); err != nil { return nil, err } @@ -64,7 +66,7 @@ func parseIndexes(namespace string, st reflect.Type, joined *map[string][]int) ( return indexDefs, nil } -func parseSchema(namespace string, st reflect.Type) *bindings.SchemaDef { +func parseSchema(st reflect.Type) *bindings.SchemaDef { reflector := &jsonschema.Reflector{} reflector.FieldIsInScheme = func(f reflect.StructField) bool { _, _, _, idxSettings := parseRxTags(f) @@ -102,19 +104,20 @@ func parseIndexesImpl(indexDefs *[]bindings.IndexDef, st reflect.Type, subArray } for i := 0; i < st.NumField(); i++ { - t := st.Field(i).Type + field := st.Field(i) + t := field.Type if t.Kind() == reflect.Ptr { t = t.Elem() } // Get and parse tags - jsonPath := strings.Split(st.Field(i).Tag.Get("json"), ",")[0] + jsonTag := strings.Split(field.Tag.Get("json"), ",")[0] - if len(jsonPath) == 0 && !st.Field(i).Anonymous { - jsonPath = st.Field(i).Name + if len(jsonTag) == 0 && !field.Anonymous { + jsonTag = field.Name } - jsonPath = jsonBasePath + jsonPath + jsonPath := jsonBasePath + jsonTag - idxName, idxType, expireAfter, idxSettings := parseRxTags(st.Field(i)) + idxName, idxType, expireAfter, idxSettings := parseRxTags(field) if idxName == "-" { continue } @@ -126,17 +129,33 @@ func parseIndexesImpl(indexDefs *[]bindings.IndexDef, st reflect.Type, subArray } if opts.isPk && strings.TrimSpace(idxName) == "" { - return fmt.Errorf("No index name is specified for primary key in field %s", st.Field(i).Name) + return fmt.Errorf("no index name is specified for primary key in field '%s'; jsonpath: '%s'", field.Name, jsonPath) } if idxType == "rtree" { if t.Kind() != reflect.Array || t.Len() != 2 || t.Elem().Kind() != reflect.Float64 { - return fmt.Errorf("'rtree' index allowed only for [2]float64 or reindexer.Point field type") + return fmt.Errorf("'rtree' index allowed only for [2]float64 or reindexer.Point field type (index name: '%s', field name: '%s', jsonpath: '%s')", + reindexPath, field.Name, jsonPath) } } - if parseByKeyWord(&idxSettings, "composite") { + + if jsonTag == "-" && !opts.isComposite && !opts.isJoined { + if reindexTag := field.Tag.Get("reindex"); reindexTag != "" { + return fmt.Errorf("non-composite/non-joined field ('%s'), marked with `json:-` can not have explicit reindex tags, but it does ('%s')", field.Name, reindexTag) + } + continue + } + if !opts.isComposite && !field.IsExported() { + if reindexTag := field.Tag.Get("reindex"); reindexTag != "" { + return fmt.Errorf("unexported non-composite field ('%s') can not have reindex tags, but it does ('%s')", field.Name, reindexTag) + } + continue + } + + if opts.isComposite { if t.Kind() != reflect.Struct || t.NumField() != 0 { - return fmt.Errorf("'composite' tag allowed only on empty on structs: Invalid tags %v on field %s", strings.SplitN(st.Field(i).Tag.Get("reindex"), ",", 3), st.Field(i).Name) + return fmt.Errorf("'composite' tag allowed only on empty on structs: Invalid tags '%v' on field '%s'", + strings.SplitN(field.Tag.Get("reindex"), ",", 3), field.Name) } indexDef := makeIndexDef(parseCompositeName(reindexPath), parseCompositeJsonPaths(reindexPath), idxType, "composite", opts, CollateNone, "", parseExpireAfter(expireAfter)) @@ -144,13 +163,17 @@ func parseIndexesImpl(indexDefs *[]bindings.IndexDef, st reflect.Type, subArray return err } } else if t.Kind() == reflect.Struct { + if opts.isJoined { + return fmt.Errorf("joined index must be a slice of structs/pointers, but it is a single struct (index name: '%s', field name: '%s', jsonpath: '%s')", + reindexPath, field.Name, jsonPath) + } if err := parseIndexesImpl(indexDefs, t, subArray, reindexPath, jsonPath, joined, parsed); err != nil { return err } } else if (t.Kind() == reflect.Slice || t.Kind() == reflect.Array) && (t.Elem().Kind() == reflect.Struct || (t.Elem().Kind() == reflect.Ptr && t.Elem().Elem().Kind() == reflect.Struct)) { // Check if field nested slice of struct - if parseByKeyWord(&idxSettings, "joined") && len(idxName) > 0 { + if opts.isJoined && len(idxName) > 0 { (*joined)[idxName] = st.Field(i).Index } else if err := parseIndexesImpl(indexDefs, t.Elem(), true, reindexPath, jsonPath, joined, parsed); err != nil { return err @@ -165,17 +188,22 @@ func parseIndexesImpl(indexDefs *[]bindings.IndexDef, st reflect.Type, subArray } if opts.isUuid { if fieldType != "string" { - return fmt.Errorf("UUID index is not applicable with '%v' field, only with 'string'", fieldType) + return fmt.Errorf("UUID index is not applicable with '%v' field, only with 'string' (index name: '%s', field name: '%s', jsonpath: '%s')", + fieldType, reindexPath, field.Name, jsonPath) } fieldType = "uuid" } + if opts.isJoined { + return fmt.Errorf("joined index must be a slice of objects/pointers, but it is a scalar value (index name: '%s', field name: '%s', jsonpath: '%s')", + reindexPath, field.Name, jsonPath) + } indexDef := makeIndexDef(reindexPath, []string{jsonPath}, idxType, fieldType, opts, collateMode, sortOrderLetters, parseExpireAfter(expireAfter)) if err := indexDefAppend(indexDefs, indexDef, opts.isAppenable); err != nil { return err } } if len(idxSettings) > 0 { - return fmt.Errorf("Unknown index settings are found: %v", idxSettings) + return fmt.Errorf("unknown index settings are found: '%v'", idxSettings) } } @@ -202,6 +230,10 @@ func parseOpts(idxSettingsBuf *[]string) indexOptions { opts.rtreeType = idxSetting case "uuid": opts.isUuid = true + case "joined": + opts.isJoined = true + case "composite": + opts.isComposite = true default: newIdxSettingsBuf = append(newIdxSettingsBuf, idxSetting) } @@ -238,7 +270,7 @@ func parseCollate(idxSettingsBuf *[]string) (int, string) { if newCollateMode, ok := collateModes[kvIdxSettings[0]]; ok { if collateMode != CollateNone { - panic(fmt.Errorf("Collate mode is already set to %d. Misunderstanding %s", collateMode, idxSetting)) + panic(fmt.Errorf("collate mode is already set to '%d'. Misunderstanding '%s'", collateMode, idxSetting)) } collateMode = newCollateMode @@ -324,14 +356,6 @@ func getJoinedField(val reflect.Value, joined map[string][]int, name string) (re return ret } -func makeFieldDef(JSONPath string, fieldType string, isArray bool) bindings.FieldDef { - return bindings.FieldDef{ - JSONPath: JSONPath, - Type: fieldType, - IsArray: isArray, - } -} - func makeIndexDef(index string, jsonPaths []string, indexType, fieldType string, opts indexOptions, collateMode int, sortOrder string, expireAfter int) bindings.IndexDef { cm := "" switch collateMode { @@ -373,19 +397,17 @@ func indexDefAppend(indexDefs *[]bindings.IndexDef, indexDef bindings.IndexDef, indexDefExists = true foundIndexDef = indexDef foundIndexPos = pos - break } } if !indexDefExists { *indexDefs = append(*indexDefs, indexDef) - return nil } if indexDef.IndexType != foundIndexDef.IndexType { - return fmt.Errorf("Index %s has another type: %+v", name, indexDef) + return fmt.Errorf("index '%s' has another type: found index def is '%+v' and new index def is '%+v'", name, foundIndexDef, indexDef) } if len(indexDef.JSONPaths) > 0 && indexDef.IndexType != "composite" { @@ -400,9 +422,8 @@ func indexDefAppend(indexDefs *[]bindings.IndexDef, indexDef bindings.IndexDef, if !isPresented { if !isAppendable { - return fmt.Errorf("Index %s is not appendable", name) + return fmt.Errorf("index '%s' is not appendable. Attempt to create array index with multiple JSON-paths: %v", name, indexDef.JSONPaths) } - foundIndexDef.JSONPaths = append(foundIndexDef.JSONPaths, indexDef.JSONPaths[0]) } diff --git a/reindexer_impl.go b/reindexer_impl.go index b4763e32f..f7958f27a 100644 --- a/reindexer_impl.go +++ b/reindexer_impl.go @@ -310,6 +310,7 @@ func (db *reindexerImpl) openNamespace(ctx context.Context, namespace string, op db.binding.DropNamespace(ctx, namespace) continue } + db.unregisterNamespaceImpl(namespace) db.binding.CloseNamespace(ctx, namespace) break } @@ -338,6 +339,12 @@ func (db *reindexerImpl) registerNamespace(ctx context.Context, namespace string return db.registerNamespaceImpl(namespace, opts, s) } +func (db *reindexerImpl) unregisterNamespaceImpl(namespace string) { + db.lock.Lock() + defer db.lock.Unlock() + delete(db.ns, namespace) +} + // registerNamespace Register go type against namespace. There are no data and indexes changes will be performed func (db *reindexerImpl) registerNamespaceImpl(namespace string, opts *NamespaceOptions, s interface{}) (err error) { t := reflect.TypeOf(s) @@ -392,10 +399,10 @@ func (db *reindexerImpl) registerNamespaceImpl(namespace string, opts *Namespace if err = validator.Validate(s); err != nil { return err } - if ns.indexes, err = parseIndexes(namespace, ns.rtype, &ns.joined); err != nil { + if ns.indexes, err = parseIndexes(ns.rtype, &ns.joined); err != nil { return err } - if schema := parseSchema(namespace, ns.rtype); schema != nil { + if schema := parseSchema(ns.rtype); schema != nil { ns.schema = *schema } @@ -418,10 +425,7 @@ func (db *reindexerImpl) dropNamespace(ctx context.Context, namespace string) er db.nsModifySlowLock.Lock() defer db.nsModifySlowLock.Unlock() - db.lock.Lock() - delete(db.ns, namespace) - db.lock.Unlock() - + db.unregisterNamespaceImpl(namespace) return db.binding.DropNamespace(ctx, namespace) } @@ -485,10 +489,7 @@ func (db *reindexerImpl) closeNamespace(ctx context.Context, namespace string) e db.nsModifySlowLock.Lock() defer db.nsModifySlowLock.Unlock() - db.lock.Lock() - delete(db.ns, namespace) - db.lock.Unlock() - + db.unregisterNamespaceImpl(namespace) return db.binding.CloseNamespace(ctx, namespace) } diff --git a/test/index_struct_test.go b/test/index_struct_test.go new file mode 100644 index 000000000..ffd37db08 --- /dev/null +++ b/test/index_struct_test.go @@ -0,0 +1,145 @@ +package reindexer + +import ( + "testing" + + "github.com/restream/reindexer/v3" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type Account struct { + Id int64 `json:"id" reindex:"id,,pk"` + SAN string `json:"san" reindex:"san,hash"` + Count string `json:"-" reindex:"count,hash"` +} + +type Orders struct { + OrderId int64 `json:"-"` + OrderOwnerFName string + OrderTime uint32 `reindex:"ordertime"` + OrderCity string `reindex:"ordercity,tree"` +} + +type TestOpenNamespace struct { + Id int64 `json:"id" reindex:"id,,pk"` + First int `reindex:"first,-" json:"first"` + Age1 int64 `json:"-"` + Age2 int64 `reindex:"-"` + Field int64 + Parent *Orders `reindex:"-"` + + privateSimple string `json:"-"` + privateSlice string `json:"-"` + privateStruct struct{} `json:"-"` + + Accounts1 []Account `json:"-" reindex:"accounts,,joined"` + Accounts2 []Account `json:"-"` + Accounts3 []Account `reindex:"accounts,,joined"` + + Obj []Orders `json:"Ord1,omitempty"` + privateObj []Orders `json:"Ord2,omitempty"` + + _ struct{} `json:"-" reindex:"id+first,,composite"` + privateComposite1 struct{} `reindex:"first+id,,composite"` +} + +type FailSimple struct { + Id int64 `json:"id" reindex:"id,,pk"` + Age string `json:"-" reindex:"age,hash"` +} + +type FailPrivate struct { + Id int64 `json:"id" reindex:"id,,pk"` + private string `json:"private" reindex:"private,hash"` +} + +type FailPrivateJoin struct { + Id int64 `json:"id" reindex:"id,,pk"` + privateAccounts []Account `json:"-" reindex:"accounts,,joined"` +} + +type FailJoinScalar struct { + Id int64 `json:"id" reindex:"id,,pk"` + Accounts string `json:"-" reindex:"accounts,,joined"` +} + +type FailJoinSingleStruct struct { + Id int64 `json:"id" reindex:"id,,pk"` + Accounts Account `json:"-" reindex:"accounts,,joined"` +} + +type FailComposite struct { + Id int64 `json:"id" reindex:"id,,pk"` + Composite []Orders `json:"-" reindex:"ordertime+ordercity,,composite"` +} + +type ExpectedIndexDef struct { + Name string + JSONPaths []string + IndexType string + FieldType string +} + +func TestOpenNs(t *testing.T) { + t.Parallel() + + t.Run("open namespase: check indexes creation", func(t *testing.T) { + if len(DB.slaveList) > 0 { + t.Skip() // This test contains ns open/close and won't work with our replication testing logic + } + + const ns = "test_namespace_open" + err := DB.OpenNamespace(ns, reindexer.DefaultNamespaceOptions(), TestOpenNamespace{}) + defer DB.CloseNamespace(ns) + require.NoError(t, err) + desc, err := DB.DescribeNamespace(ns) + require.NoError(t, err) + actual := make([]ExpectedIndexDef, 0) + for _, idx := range desc.Indexes { + actual = append(actual, ExpectedIndexDef{ + idx.IndexDef.Name, + idx.IndexDef.JSONPaths, + idx.IndexDef.IndexType, + idx.IndexDef.FieldType, + }) + } + expected := []ExpectedIndexDef{ + {Name: "id", JSONPaths: []string{"id"}, IndexType: "hash", FieldType: "int64"}, + {Name: "first", JSONPaths: []string{"first"}, IndexType: "-", FieldType: "int64"}, + {Name: "ordertime", JSONPaths: []string{"Ord1.OrderTime"}, IndexType: "hash", FieldType: "int"}, + {Name: "ordercity", JSONPaths: []string{"Ord1.OrderCity"}, IndexType: "tree", FieldType: "string"}, + {Name: "id+first", JSONPaths: []string{"id", "first"}, IndexType: "hash", FieldType: "composite"}, + {Name: "first+id", JSONPaths: []string{"first", "id"}, IndexType: "hash", FieldType: "composite"}, + } + assert.Equal(t, expected, actual) + }) + + t.Run("no open namespace: check indexes are not created", func(t *testing.T) { + const ns = "test_no_namespace_open" + + err := DB.OpenNamespace(ns, reindexer.DefaultNamespaceOptions(), FailSimple{}) + assert.ErrorContains(t, err, + "non-composite/non-joined field ('Age'), marked with `json:-` can not have explicit reindex tags, but it does ('age,hash')") + + err = DB.OpenNamespace(ns, reindexer.DefaultNamespaceOptions(), FailPrivate{}) + assert.ErrorContains(t, err, + "unexported non-composite field ('private') can not have reindex tags, but it does ('private,hash')") + + err = DB.OpenNamespace(ns, reindexer.DefaultNamespaceOptions(), FailPrivateJoin{}) + assert.ErrorContains(t, err, + "unexported non-composite field ('privateAccounts') can not have reindex tags, but it does ('accounts,,joined')") + + err = DB.OpenNamespace(ns, reindexer.DefaultNamespaceOptions(), FailJoinScalar{}) + assert.ErrorContains(t, err, + "joined index must be a slice of objects/pointers, but it is a scalar value") + + err = DB.OpenNamespace(ns, reindexer.DefaultNamespaceOptions(), FailJoinSingleStruct{}) + assert.ErrorContains(t, err, + "joined index must be a slice of structs/pointers, but it is a single struct") + + err = DB.OpenNamespace(ns, reindexer.DefaultNamespaceOptions(), FailComposite{}) + assert.ErrorContains(t, err, + "'composite' tag allowed only on empty on structs: Invalid tags '[ordertime+ordercity composite]' on field 'Composite'") + }) +} diff --git a/test/uuid_test.go b/test/uuid_test.go index 4316030e3..dc7c3dca8 100644 --- a/test/uuid_test.go +++ b/test/uuid_test.go @@ -240,25 +240,25 @@ func TestNotStringItemsWithUuidTag(t *testing.T) { nsOpts := reindexer.DefaultNamespaceOptions() t.Run("cant open ns when int field with uuid tag", func(t *testing.T) { - errExpr := "UUID index is not applicable with 'int64' field, only with 'string'" + errExpr := "UUID index is not applicable with 'int64' field, only with 'string' (index name: 'uuid', field name: 'Uuid', jsonpath: 'uuid')" assert.EqualError(t, DB.OpenNamespace(TestIntItemWithUuidTagNs, nsOpts, TestIntItemWithUuidTagStruct{}), errExpr) }) t.Run("cant open ns when int64 field with uuid tag", func(t *testing.T) { - errExpr := "UUID index is not applicable with 'int64' field, only with 'string'" + errExpr := "UUID index is not applicable with 'int64' field, only with 'string' (index name: 'uuid', field name: 'Uuid', jsonpath: 'uuid')" assert.EqualError(t, DB.OpenNamespace(TestInt64ItemWithUuidTagNs, nsOpts, TestInt64ItemWithUuidTagStruct{}), errExpr) }) t.Run("cant open ns when float field with uuid tag", func(t *testing.T) { - errExpr := "UUID index is not applicable with 'double' field, only with 'string'" + errExpr := "UUID index is not applicable with 'double' field, only with 'string' (index name: 'uuid', field name: 'Uuid', jsonpath: 'uuid')" assert.EqualError(t, DB.OpenNamespace(TestFloatItemWithUuidTagNs, nsOpts, TestFloatItemWithUuidTagStruct{}), errExpr) }) t.Run("cant open ns when double field with uuid tag", func(t *testing.T) { - errExpr := "UUID index is not applicable with 'double' field, only with 'string'" + errExpr := "UUID index is not applicable with 'double' field, only with 'string' (index name: 'uuid', field name: 'Uuid', jsonpath: 'uuid')" assert.EqualError(t, DB.OpenNamespace(TestDoubleItemWithUuidTagNs, nsOpts, TestDoubleItemWithUuidTagStruct{}), errExpr) }) @@ -270,13 +270,13 @@ func TestNotStringItemsWithUuidTag(t *testing.T) { }) t.Run("cant open ns when byte field with uuid tag", func(t *testing.T) { - errExpr := "UUID index is not applicable with 'int' field, only with 'string'" + errExpr := "UUID index is not applicable with 'int' field, only with 'string' (index name: 'uuid', field name: 'Uuid', jsonpath: 'uuid')" assert.EqualError(t, DB.OpenNamespace(TestByteItemWithUuidTagNs, nsOpts, TestByteItemWithUuidTagStruct{}), errExpr) }) t.Run("cant open ns when bool field with uuid tag", func(t *testing.T) { - errExpr := "UUID index is not applicable with 'bool' field, only with 'string'" + errExpr := "UUID index is not applicable with 'bool' field, only with 'string' (index name: 'uuid', field name: 'Uuid', jsonpath: 'uuid')" assert.EqualError(t, DB.OpenNamespace(TestBoolItemWithUuidTagNs, nsOpts, TestBoolItemWithUuidTagStruct{}), errExpr) })